我想从C#更改ScrollViewer
ListBox
的属性。
我在Stackoverflow上找到了this question。我接受了接受的答案的建议,并将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);
}
}
}
当我点击“Check ScrollViewer”按钮时,它会打印“null”。即,ScrollViewer
未被检索。
我如何才能找到ScrollViewer
? : - )
答案 0 :(得分:22)
你可以试试这个小助手功能
使用
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;
}
希望这有帮助
答案 1 :(得分:8)
如果你将使用标准的ListBox,那么你可以改变你的getter:
public class MyListBox : ListBox
{
public ScrollViewer ScrollViewer
{
get
{
Border border = (Border)VisualTreeHelper.GetChild(this, 0);
return (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
}
}
}
答案 2 :(得分:5)
我修改了@ 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))
答案 3 :(得分:1)
至于我,将ScrollViewer作为属性公开是个坏主意。首先,无法保证ScrollViewer存在于模板中。其次,ScrollViewer与ItemsPanel和ItemContainerGenerator同步工作。覆盖这是不常见行为的直接方式。
WPF控件使用其他模式。他们的类就像外部逻辑用法和内部视觉表现之间的调解者。 ListBox应该公开ScrollViewer可以在模板中使用的属性,但不能暴露ScrollViewer。通过这样做,您可以破坏WPF标准,将控制权限制为特定模板,并允许用户代码破解内部ListBox实现。
答案 4 :(得分:0)
这是@ 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;
}
}
答案 5 :(得分:0)
ScrollViewer的属性“附加”到列表框(参见https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview)。您可以通过以下函数获取或设置其依赖项属性:
import turbodbc
connection = turbodbc.connect(
driver=driver , server=server , database=database,
uid=username , pwd=password,
)
connection.autocommit =True
cur = connection.cursor()
cur.execute("create database newdb")
和
public object GetValue (System.Windows.DependencyProperty dp);
例如,对于列表框“ lb”,您可以编写:
public void SetValue (System.Windows.DependencyProperty dp, object value);
要么
lb.GetValue(ScrollViewer.ActualHeightProperty);