从 C# 访问列表框的滚动查看器
本文关键字:滚动 访问 列表 | 更新日期: 2023-09-27 18:32:07
我想从 C# 更改ListBox
ScrollViewer
的属性。
我在Stackoverflow上发现了这个问题。我接受了接受答案的建议,并将ScrollViewer
暴露为子类的属性。但是,这在下面显示的示例中似乎不起作用。该问题中的一些评论还指出,这种技术不起作用。
XAML:
<Window x:Class="StackoverflowListBoxScrollViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Controls;
namespace StackoverflowListBoxScrollViewer
{
public class MyListBox : ListBox
{
public ScrollViewer ScrollViewer
{ get { return (ScrollViewer)GetTemplateChild("ScrollViewer"); } }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var myListBox = new MyListBox();
Content = myListBox;
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
myListBox.Items.Add(new Button() { Content = "abc" });
var button = new Button() { Content = "Check ScrollViewer" };
button.Click += (s, e) =>
{
if (myListBox.ScrollViewer == null)
Console.WriteLine("null");
};
myListBox.Items.Add(button);
}
}
}
当我单击"检查滚动查看器"按钮时,它打印"null"。 即,未检索ScrollViewer
。
我怎么去那个的ScrollViewer
? :-)
你可以试试这个小辅助函数
用法
var scrollViewer = GetDescendantByType(yourListBox, typeof(ScrollViewer)) as ScrollViewer;
帮助程序函数
public static Visual GetDescendantByType(Visual element, Type type)
{
if (element == null) {
return null;
}
if (element.GetType() == type) {
return element;
}
Visual foundElement = null;
if (element is FrameworkElement) {
(element as FrameworkElement).ApplyTemplate();
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) {
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = GetDescendantByType(visual, type);
if (foundElement != null) {
break;
}
}
return foundElement;
}
希望这有帮助
如果你将使用标准ListBox,所以你可以把你的getter更改为这个:
public class MyListBox : ListBox
{
public ScrollViewer ScrollViewer
{
get
{
Border border = (Border)VisualTreeHelper.GetChild(this, 0);
return (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
}
}
}
我已经修改了@punker76的伟大答案,为Visual 创建一个扩展方法并提供显式返回类型:
public static class Extensions
{
public static T GetDescendantByType<T>(this Visual element) where T:class
{
if (element == null)
{
return default(T);
}
if (element.GetType() == typeof(T))
{
return element as T;
}
T foundElement = null;
if (element is FrameworkElement)
{
(element as FrameworkElement).ApplyTemplate();
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = visual.GetDescendantByType<T>();
if (foundElement != null)
{
break;
}
}
return foundElement;
}
}
您现在可以通过 SomeVisual.GetDescendantByType 调用它,它返回已经正确类型的 ScrollViewer 或 null(默认 (T))
对我来说,将 ScrollViewer 公开为一个属性是一个坏主意。首先,不能保证模板中存在滚动查看器。其次,ScrollViewer与ItemsPanel和ItemContainerGenerator同步工作。覆盖这一点是导致不常见行为的直接方法。
WPF 控件使用另一种模式。它们的类就像外部逻辑用法和内部视觉表示之间的中介。列表框应公开可由模板中的滚动查看器使用的属性,但不能公开滚动查看器使用的属性。通过这样做,可以破坏 WPF 标准,将控件限制为特定模板,并允许用户代码破解内部 ListBox 实现。
ScrollViewer 的属性"附加"到 ListBox (参见 https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview)。您可以通过以下函数获取或设置为依赖项属性:
public object GetValue (System.Windows.DependencyProperty dp);
和
public void SetValue (System.Windows.DependencyProperty dp, object value);
例如,对于列表框"lb",您可以编写:
lb.GetValue(ScrollViewer.ActualHeightProperty);
或 lb.SetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Visible);
这是 @punker76 对 C# 6 的答案的另一个重新设计和通用版本:
public static class VisualExtensions
{
public static T FindVisualDescendant<T>(this Visual element) where T : Visual
{
if (element == null)
return null;
var e = element as T;
if (e != null)
return e;
(element as FrameworkElement)?.ApplyTemplate();
var childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (var i = 0; i < childrenCount; i++)
{
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
var foundElement = visual.FindVisualDescendant<T>();
if (foundElement != null)
return foundElement;
}
return null;
}
}