是否可以阻止第一个条目在 Xamarin Forms 的 ScrollView 中获得焦点
Is it possible to stop the first Entry getting Focus in a ScrollView in Xamarin Forms
在我的申请中我有以下情况:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:conv="clr-namespace:NumericTextBoxTest.Converters;assembly=NumericTextBoxTest"
xmlns:numericTextBox="clr-namespace:Syncfusion.SfNumericTextBox.XForms;assembly=Syncfusion.SfNumericTextBox.XForms"
x:Class="NumericTextBoxTest.MainPage">
<ScrollView>
<StackLayout>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
</StackLayout>
</ScrollView>
</ContentPage>
现在,如果我单击底部(条目下方)的空白 space,即 ScrollView
,ScrollView
中的第一个 Entry
将获得焦点。
非常烦人如果我在第一个 Entry
上更改值并试图分散 Entry
的焦点来设置值。
是否可以阻止这种行为?
Now If I click the blank space at the bottom (below the entries) i.e. the ScrollView the first Entry in the ScrollView will gain focus.
在 UWP 中,根据设计,当 StackLayout
被点击时,系统将搜索 StackLayout
中的元素 for-each 直到第一个可以被关注的元素。作为解决此问题的变通方法,您可以在 StackLayout
的顶部放置一个不可见的按钮。
<ScrollView>
<StackLayout>
<Button HeightRequest="0" WidthRequest="1" />
<Entry />
....
<Entry />
</StackLayout>
</ScrollView>
当点击 StackLayout
时,按钮将被聚焦。 Entry
不会被聚焦
If I click the blank space at the bottom (below the entries)
您可以拉伸堆栈布局,然后它将获得焦点而不是滚动视图。
您还可以为 stacklayout 设置背景颜色,以确保 stacklayout 被拉伸
最后,我实际上最终覆盖了默认值 ScrollViewRenderer
,如评论中所述:
https://github.com/microsoft/microsoft-ui-xaml/issues/597#issuecomment-513804526
我在 UWP 上的 ScrollViewRenderer
看起来像这样:
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ScrollBarVisibility = Xamarin.Forms.ScrollBarVisibility;
using UwpScrollBarVisibility = Windows.UI.Xaml.Controls.ScrollBarVisibility;
using Size = Xamarin.Forms.Size;
using Point = Xamarin.Forms.Point;
using Thickness = Xamarin.Forms.Thickness;
using FieldStrikeMove.Forms.CustomControls;
//https://github.com/microsoft/microsoft-ui-xaml/issues/597
//https://github.com/xamarin/Xamarin.Forms/blob/f17fac7b9e2225b1bfe9e94909d2b954106f8f1f/Xamarin.Forms.Platform.UAP/ScrollViewRenderer.cs
//07/01/20
[assembly: ExportRenderer(typeof(ExtendedScrollView), typeof(MyApp.UWP.CustomRenderers.Controls.ScrollViewRenderer))]
[assembly: ExportRenderer(typeof(ScrollView), typeof(MyApp.UWP.CustomRenderers.Controls.ScrollViewRenderer))]
namespace MApp.UWP.CustomRenderers.Controls
{
public class ScrollViewRenderer : ViewRenderer<ScrollView, ScrollViewer>//, IDontGetFocus
{
VisualElement _currentView;
bool _checkedForRtlScroll = false;
public ScrollViewRenderer()
{
AutoPackage = false;
}
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
SizeRequest result = base.GetDesiredSize(widthConstraint, heightConstraint);
result.Minimum = new Size(40, 40);
return result;
}
protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
{
if (Element == null)
return finalSize;
Element.IsInNativeLayout = true;
Control?.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
Element.IsInNativeLayout = false;
return finalSize;
}
protected override void Dispose(bool disposing)
{
CleanUp(Element, Control);
base.Dispose(disposing);
}
protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
{
if (Element == null)
return new Windows.Foundation.Size(0, 0);
double width = Math.Max(0, Element.Width);
double height = Math.Max(0, Element.Height);
var result = new Windows.Foundation.Size(width, height);
Control?.Measure(result);
return result;
}
void CleanUp(ScrollView scrollView, ScrollViewer scrollViewer)
{
if (Element != null)
Element.PropertyChanged -= OnContentElementPropertyChanged;
if (ContainerElement != null)
ContainerElement.LayoutUpdated -= SetInitialRtlPosition;
if (scrollView != null)
{
scrollView.ScrollToRequested -= OnScrollToRequested;
}
if (scrollViewer != null)
{
scrollViewer.ViewChanged -= OnViewChanged;
if (scrollViewer.Content is FrameworkElement element)
{
element.LayoutUpdated -= SetInitialRtlPosition;
}
}
if (_currentView != null)
_currentView.Cleanup();
}
protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> e)
{
base.OnElementChanged(e);
CleanUp(e.OldElement, Control);
if (e.NewElement != null)
{
if (Control == null)
{
SetNativeControl(new ScrollViewer
{
HorizontalScrollBarVisibility = ScrollBarVisibilityToUwp(e.NewElement.HorizontalScrollBarVisibility),
VerticalScrollBarVisibility = ScrollBarVisibilityToUwp(e.NewElement.VerticalScrollBarVisibility),
});
Control.ViewChanged += OnViewChanged;
}
Element.ScrollToRequested += OnScrollToRequested;
UpdateOrientation();
UpdateContent();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "Content")
UpdateContent();
else if (e.PropertyName == Layout.PaddingProperty.PropertyName)
UpdateContentMargins();
else if (e.PropertyName == ScrollView.OrientationProperty.PropertyName)
UpdateOrientation();
else if (e.PropertyName == ScrollView.VerticalScrollBarVisibilityProperty.PropertyName)
UpdateVerticalScrollBarVisibility();
else if (e.PropertyName == ScrollView.HorizontalScrollBarVisibilityProperty.PropertyName)
UpdateHorizontalScrollBarVisibility();
}
protected void OnContentElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == View.MarginProperty.PropertyName)
UpdateContentMargins();
}
void UpdateContent()
{
if (_currentView != null)
_currentView.Cleanup();
if (Control?.Content is FrameworkElement oldElement)
{
oldElement.LayoutUpdated -= SetInitialRtlPosition;
if (oldElement is IVisualElementRenderer oldRenderer
&& oldRenderer.Element is View oldContentView)
oldContentView.PropertyChanged -= OnContentElementPropertyChanged;
}
_currentView = Element.Content;
IVisualElementRenderer renderer = null;
if (_currentView != null)
renderer = _currentView.GetOrCreateRenderer();
Control.Content = renderer != null ? renderer.ContainerElement : null;
UpdateContentMargins();
if (renderer?.Element != null)
renderer.Element.PropertyChanged += OnContentElementPropertyChanged;
if (renderer?.ContainerElement != null)
renderer.ContainerElement.LayoutUpdated += SetInitialRtlPosition;
}
async void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
{
ClearRtlScrollCheck();
// Adding items into the view while scrolling to the end can cause it to fail, as
// the items have not actually been laid out and return incorrect scroll position
// values. The ScrollViewRenderer for Android does something similar by waiting up
// to 10ms for layout to occur.
int cycle = 0;
while (Element != null && !Element.IsInNativeLayout)
{
await Task.Delay(TimeSpan.FromMilliseconds(1));
cycle++;
if (cycle >= 10)
break;
}
if (Element == null)
return;
double x = e.ScrollX, y = e.ScrollY;
ScrollToMode mode = e.Mode;
if (mode == ScrollToMode.Element)
{
Point pos = Element.GetScrollPositionForElement((VisualElement)e.Element, e.Position);
x = pos.X;
y = pos.Y;
mode = ScrollToMode.Position;
}
if (mode == ScrollToMode.Position)
{
Control.ChangeView(x, y, null, !e.ShouldAnimate);
}
Element.SendScrollFinished();
}
void SetInitialRtlPosition(object sender, object e)
{
if (Control == null) return;
if (Control.ActualWidth <= 0 || _checkedForRtlScroll || Control.Content == null)
return;
if (Element is IVisualElementController controller && controller.EffectiveFlowDirection.IsLeftToRight())
{
ClearRtlScrollCheck();
return;
}
var element = (Control.Content as FrameworkElement);
if (element.ActualWidth == Control.ActualWidth)
return;
ClearRtlScrollCheck();
Control.ChangeView(element.ActualWidth, 0, null, true);
}
void ClearRtlScrollCheck()
{
_checkedForRtlScroll = true;
if (Control.Content is FrameworkElement element)
element.LayoutUpdated -= SetInitialRtlPosition;
}
void OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ClearRtlScrollCheck();
Element.SetScrolledPosition(Control.HorizontalOffset, Control.VerticalOffset);
if (!e.IsIntermediate)
Element.SendScrollFinished();
}
Windows.UI.Xaml.Thickness AddMargin(Thickness original, double left, double top, double right, double bottom)
{
return new Windows.UI.Xaml.Thickness(original.Left + left, original.Top + top, original.Right + right, original.Bottom + bottom);
}
// UAP ScrollView forces Content origin to be the same as the ScrollView origin.
// This prevents Forms layout from emulating Padding and Margin by offsetting the origin.
// So we must actually set the UAP Margin property instead of emulating it with an origin offset.
// Not only that, but in UAP Padding and Margin are aliases with
// the former living on the parent and the latter on the child.
// So that's why the UAP Margin is set to the sum of the Forms Padding and Forms Margin.
void UpdateContentMargins()
{
if (!(Control.Content is FrameworkElement element
&& element is IVisualElementRenderer renderer
&& renderer.Element is View contentView))
return;
var margin = contentView.Margin;
var padding = Element.Padding;
switch (Element.Orientation)
{
case ScrollOrientation.Horizontal:
// need to add left/right margins
element.Margin = AddMargin(margin, padding.Left, 0, padding.Right, 0);
break;
case ScrollOrientation.Vertical:
// need to add top/bottom margins
element.Margin = AddMargin(margin, 0, padding.Top, 0, padding.Bottom);
break;
case ScrollOrientation.Both:
// need to add all margins
element.Margin = AddMargin(margin, padding.Left, padding.Top, padding.Right, padding.Bottom);
break;
}
}
void UpdateOrientation()
{
//Only update the horizontal scroll bar visibility if the user has not set a desired state.
if (Element.HorizontalScrollBarVisibility != ScrollBarVisibility.Default)
return;
var orientation = Element.Orientation;
if (orientation == ScrollOrientation.Horizontal || orientation == ScrollOrientation.Both)
{
Control.HorizontalScrollBarVisibility = UwpScrollBarVisibility.Auto;
}
else
{
Control.HorizontalScrollBarVisibility = UwpScrollBarVisibility.Disabled;
}
}
UwpScrollBarVisibility ScrollBarVisibilityToUwp(ScrollBarVisibility visibility)
{
switch (visibility)
{
case ScrollBarVisibility.Always:
return UwpScrollBarVisibility.Visible;
case ScrollBarVisibility.Default:
return UwpScrollBarVisibility.Auto;
case ScrollBarVisibility.Never:
return UwpScrollBarVisibility.Hidden;
default:
return UwpScrollBarVisibility.Auto;
}
}
void UpdateVerticalScrollBarVisibility()
{
Control.VerticalScrollBarVisibility = ScrollBarVisibilityToUwp(Element.VerticalScrollBarVisibility);
}
void UpdateHorizontalScrollBarVisibility()
{
var horizontalVisibility = Element.HorizontalScrollBarVisibility;
if (horizontalVisibility == ScrollBarVisibility.Default)
{
UpdateOrientation();
return;
}
var orientation = Element.Orientation;
if (orientation == ScrollOrientation.Horizontal || orientation == ScrollOrientation.Both)
Control.HorizontalScrollBarVisibility = ScrollBarVisibilityToUwp(horizontalVisibility);
}
}
public static class Extensions
{
internal static void Cleanup(this VisualElement self)
{
if (self == null)
throw new ArgumentNullException("self");
IVisualElementRenderer renderer = Platform.GetRenderer(self);
foreach (Element element in self.Descendants())
{
var visual = element as VisualElement;
if (visual == null)
continue;
IVisualElementRenderer childRenderer = Platform.GetRenderer(visual);
if (childRenderer != null)
{
childRenderer.Dispose();
Platform.SetRenderer(visual, null);
}
}
if (renderer != null)
{
renderer.Dispose();
Platform.SetRenderer(self, null);
}
}
}
}
[更新]
我刚刚发现当 Class 具有与 Xamarin Forms 中的名称相同。
现在一切正常
[原创]
我尝试实现 User1 发布的 CustomRenderer,但它似乎没有在 UWP 项目中使用。 ScrollViewRenderer
的构造函数从未被调用,尽管我在页面上有一个 ScrollView
。
如果我从 ScrollView
派生一个 CustomScrollView
并让渲染器应用于 CustomScrollView
它被使用并且解决方法按预期工作但我似乎无法覆盖所有 ScrollView 的渲染器。
有什么提示为什么不使用渲染器来替换 XF 的默认内置 ScrollViewRenderer 吗? (顺便说一句。我使用的是 XF 5.0.0.1874)
在我的申请中我有以下情况:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:conv="clr-namespace:NumericTextBoxTest.Converters;assembly=NumericTextBoxTest"
xmlns:numericTextBox="clr-namespace:Syncfusion.SfNumericTextBox.XForms;assembly=Syncfusion.SfNumericTextBox.XForms"
x:Class="NumericTextBoxTest.MainPage">
<ScrollView>
<StackLayout>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
<Entry/>
</StackLayout>
</ScrollView>
</ContentPage>
现在,如果我单击底部(条目下方)的空白 space,即 ScrollView
,ScrollView
中的第一个 Entry
将获得焦点。
非常烦人如果我在第一个 Entry
上更改值并试图分散 Entry
的焦点来设置值。
是否可以阻止这种行为?
Now If I click the blank space at the bottom (below the entries) i.e. the ScrollView the first Entry in the ScrollView will gain focus.
在 UWP 中,根据设计,当 StackLayout
被点击时,系统将搜索 StackLayout
中的元素 for-each 直到第一个可以被关注的元素。作为解决此问题的变通方法,您可以在 StackLayout
的顶部放置一个不可见的按钮。
<ScrollView>
<StackLayout>
<Button HeightRequest="0" WidthRequest="1" />
<Entry />
....
<Entry />
</StackLayout>
</ScrollView>
当点击 StackLayout
时,按钮将被聚焦。 Entry
不会被聚焦
If I click the blank space at the bottom (below the entries)
您可以拉伸堆栈布局,然后它将获得焦点而不是滚动视图。
您还可以为 stacklayout 设置背景颜色,以确保 stacklayout 被拉伸
最后,我实际上最终覆盖了默认值 ScrollViewRenderer
,如评论中所述:
https://github.com/microsoft/microsoft-ui-xaml/issues/597#issuecomment-513804526
我在 UWP 上的 ScrollViewRenderer
看起来像这样:
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ScrollBarVisibility = Xamarin.Forms.ScrollBarVisibility;
using UwpScrollBarVisibility = Windows.UI.Xaml.Controls.ScrollBarVisibility;
using Size = Xamarin.Forms.Size;
using Point = Xamarin.Forms.Point;
using Thickness = Xamarin.Forms.Thickness;
using FieldStrikeMove.Forms.CustomControls;
//https://github.com/microsoft/microsoft-ui-xaml/issues/597
//https://github.com/xamarin/Xamarin.Forms/blob/f17fac7b9e2225b1bfe9e94909d2b954106f8f1f/Xamarin.Forms.Platform.UAP/ScrollViewRenderer.cs
//07/01/20
[assembly: ExportRenderer(typeof(ExtendedScrollView), typeof(MyApp.UWP.CustomRenderers.Controls.ScrollViewRenderer))]
[assembly: ExportRenderer(typeof(ScrollView), typeof(MyApp.UWP.CustomRenderers.Controls.ScrollViewRenderer))]
namespace MApp.UWP.CustomRenderers.Controls
{
public class ScrollViewRenderer : ViewRenderer<ScrollView, ScrollViewer>//, IDontGetFocus
{
VisualElement _currentView;
bool _checkedForRtlScroll = false;
public ScrollViewRenderer()
{
AutoPackage = false;
}
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
SizeRequest result = base.GetDesiredSize(widthConstraint, heightConstraint);
result.Minimum = new Size(40, 40);
return result;
}
protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
{
if (Element == null)
return finalSize;
Element.IsInNativeLayout = true;
Control?.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
Element.IsInNativeLayout = false;
return finalSize;
}
protected override void Dispose(bool disposing)
{
CleanUp(Element, Control);
base.Dispose(disposing);
}
protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
{
if (Element == null)
return new Windows.Foundation.Size(0, 0);
double width = Math.Max(0, Element.Width);
double height = Math.Max(0, Element.Height);
var result = new Windows.Foundation.Size(width, height);
Control?.Measure(result);
return result;
}
void CleanUp(ScrollView scrollView, ScrollViewer scrollViewer)
{
if (Element != null)
Element.PropertyChanged -= OnContentElementPropertyChanged;
if (ContainerElement != null)
ContainerElement.LayoutUpdated -= SetInitialRtlPosition;
if (scrollView != null)
{
scrollView.ScrollToRequested -= OnScrollToRequested;
}
if (scrollViewer != null)
{
scrollViewer.ViewChanged -= OnViewChanged;
if (scrollViewer.Content is FrameworkElement element)
{
element.LayoutUpdated -= SetInitialRtlPosition;
}
}
if (_currentView != null)
_currentView.Cleanup();
}
protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> e)
{
base.OnElementChanged(e);
CleanUp(e.OldElement, Control);
if (e.NewElement != null)
{
if (Control == null)
{
SetNativeControl(new ScrollViewer
{
HorizontalScrollBarVisibility = ScrollBarVisibilityToUwp(e.NewElement.HorizontalScrollBarVisibility),
VerticalScrollBarVisibility = ScrollBarVisibilityToUwp(e.NewElement.VerticalScrollBarVisibility),
});
Control.ViewChanged += OnViewChanged;
}
Element.ScrollToRequested += OnScrollToRequested;
UpdateOrientation();
UpdateContent();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "Content")
UpdateContent();
else if (e.PropertyName == Layout.PaddingProperty.PropertyName)
UpdateContentMargins();
else if (e.PropertyName == ScrollView.OrientationProperty.PropertyName)
UpdateOrientation();
else if (e.PropertyName == ScrollView.VerticalScrollBarVisibilityProperty.PropertyName)
UpdateVerticalScrollBarVisibility();
else if (e.PropertyName == ScrollView.HorizontalScrollBarVisibilityProperty.PropertyName)
UpdateHorizontalScrollBarVisibility();
}
protected void OnContentElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == View.MarginProperty.PropertyName)
UpdateContentMargins();
}
void UpdateContent()
{
if (_currentView != null)
_currentView.Cleanup();
if (Control?.Content is FrameworkElement oldElement)
{
oldElement.LayoutUpdated -= SetInitialRtlPosition;
if (oldElement is IVisualElementRenderer oldRenderer
&& oldRenderer.Element is View oldContentView)
oldContentView.PropertyChanged -= OnContentElementPropertyChanged;
}
_currentView = Element.Content;
IVisualElementRenderer renderer = null;
if (_currentView != null)
renderer = _currentView.GetOrCreateRenderer();
Control.Content = renderer != null ? renderer.ContainerElement : null;
UpdateContentMargins();
if (renderer?.Element != null)
renderer.Element.PropertyChanged += OnContentElementPropertyChanged;
if (renderer?.ContainerElement != null)
renderer.ContainerElement.LayoutUpdated += SetInitialRtlPosition;
}
async void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
{
ClearRtlScrollCheck();
// Adding items into the view while scrolling to the end can cause it to fail, as
// the items have not actually been laid out and return incorrect scroll position
// values. The ScrollViewRenderer for Android does something similar by waiting up
// to 10ms for layout to occur.
int cycle = 0;
while (Element != null && !Element.IsInNativeLayout)
{
await Task.Delay(TimeSpan.FromMilliseconds(1));
cycle++;
if (cycle >= 10)
break;
}
if (Element == null)
return;
double x = e.ScrollX, y = e.ScrollY;
ScrollToMode mode = e.Mode;
if (mode == ScrollToMode.Element)
{
Point pos = Element.GetScrollPositionForElement((VisualElement)e.Element, e.Position);
x = pos.X;
y = pos.Y;
mode = ScrollToMode.Position;
}
if (mode == ScrollToMode.Position)
{
Control.ChangeView(x, y, null, !e.ShouldAnimate);
}
Element.SendScrollFinished();
}
void SetInitialRtlPosition(object sender, object e)
{
if (Control == null) return;
if (Control.ActualWidth <= 0 || _checkedForRtlScroll || Control.Content == null)
return;
if (Element is IVisualElementController controller && controller.EffectiveFlowDirection.IsLeftToRight())
{
ClearRtlScrollCheck();
return;
}
var element = (Control.Content as FrameworkElement);
if (element.ActualWidth == Control.ActualWidth)
return;
ClearRtlScrollCheck();
Control.ChangeView(element.ActualWidth, 0, null, true);
}
void ClearRtlScrollCheck()
{
_checkedForRtlScroll = true;
if (Control.Content is FrameworkElement element)
element.LayoutUpdated -= SetInitialRtlPosition;
}
void OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ClearRtlScrollCheck();
Element.SetScrolledPosition(Control.HorizontalOffset, Control.VerticalOffset);
if (!e.IsIntermediate)
Element.SendScrollFinished();
}
Windows.UI.Xaml.Thickness AddMargin(Thickness original, double left, double top, double right, double bottom)
{
return new Windows.UI.Xaml.Thickness(original.Left + left, original.Top + top, original.Right + right, original.Bottom + bottom);
}
// UAP ScrollView forces Content origin to be the same as the ScrollView origin.
// This prevents Forms layout from emulating Padding and Margin by offsetting the origin.
// So we must actually set the UAP Margin property instead of emulating it with an origin offset.
// Not only that, but in UAP Padding and Margin are aliases with
// the former living on the parent and the latter on the child.
// So that's why the UAP Margin is set to the sum of the Forms Padding and Forms Margin.
void UpdateContentMargins()
{
if (!(Control.Content is FrameworkElement element
&& element is IVisualElementRenderer renderer
&& renderer.Element is View contentView))
return;
var margin = contentView.Margin;
var padding = Element.Padding;
switch (Element.Orientation)
{
case ScrollOrientation.Horizontal:
// need to add left/right margins
element.Margin = AddMargin(margin, padding.Left, 0, padding.Right, 0);
break;
case ScrollOrientation.Vertical:
// need to add top/bottom margins
element.Margin = AddMargin(margin, 0, padding.Top, 0, padding.Bottom);
break;
case ScrollOrientation.Both:
// need to add all margins
element.Margin = AddMargin(margin, padding.Left, padding.Top, padding.Right, padding.Bottom);
break;
}
}
void UpdateOrientation()
{
//Only update the horizontal scroll bar visibility if the user has not set a desired state.
if (Element.HorizontalScrollBarVisibility != ScrollBarVisibility.Default)
return;
var orientation = Element.Orientation;
if (orientation == ScrollOrientation.Horizontal || orientation == ScrollOrientation.Both)
{
Control.HorizontalScrollBarVisibility = UwpScrollBarVisibility.Auto;
}
else
{
Control.HorizontalScrollBarVisibility = UwpScrollBarVisibility.Disabled;
}
}
UwpScrollBarVisibility ScrollBarVisibilityToUwp(ScrollBarVisibility visibility)
{
switch (visibility)
{
case ScrollBarVisibility.Always:
return UwpScrollBarVisibility.Visible;
case ScrollBarVisibility.Default:
return UwpScrollBarVisibility.Auto;
case ScrollBarVisibility.Never:
return UwpScrollBarVisibility.Hidden;
default:
return UwpScrollBarVisibility.Auto;
}
}
void UpdateVerticalScrollBarVisibility()
{
Control.VerticalScrollBarVisibility = ScrollBarVisibilityToUwp(Element.VerticalScrollBarVisibility);
}
void UpdateHorizontalScrollBarVisibility()
{
var horizontalVisibility = Element.HorizontalScrollBarVisibility;
if (horizontalVisibility == ScrollBarVisibility.Default)
{
UpdateOrientation();
return;
}
var orientation = Element.Orientation;
if (orientation == ScrollOrientation.Horizontal || orientation == ScrollOrientation.Both)
Control.HorizontalScrollBarVisibility = ScrollBarVisibilityToUwp(horizontalVisibility);
}
}
public static class Extensions
{
internal static void Cleanup(this VisualElement self)
{
if (self == null)
throw new ArgumentNullException("self");
IVisualElementRenderer renderer = Platform.GetRenderer(self);
foreach (Element element in self.Descendants())
{
var visual = element as VisualElement;
if (visual == null)
continue;
IVisualElementRenderer childRenderer = Platform.GetRenderer(visual);
if (childRenderer != null)
{
childRenderer.Dispose();
Platform.SetRenderer(visual, null);
}
}
if (renderer != null)
{
renderer.Dispose();
Platform.SetRenderer(self, null);
}
}
}
}
[更新]
我刚刚发现当 Class 具有与 Xamarin Forms 中的名称相同。
现在一切正常
[原创]
我尝试实现 User1 发布的 CustomRenderer,但它似乎没有在 UWP 项目中使用。 ScrollViewRenderer
的构造函数从未被调用,尽管我在页面上有一个 ScrollView
。
如果我从 ScrollView
派生一个 CustomScrollView
并让渲染器应用于 CustomScrollView
它被使用并且解决方法按预期工作但我似乎无法覆盖所有 ScrollView 的渲染器。
有什么提示为什么不使用渲染器来替换 XF 的默认内置 ScrollViewRenderer 吗? (顺便说一句。我使用的是 XF 5.0.0.1874)