【问题标题】:Accessing the ScrollViewer of a ListBox from C#从 C# 访问 ListBox 的 ScrollViewer
【发布时间】:2012-04-24 07:06:32
【问题描述】:

我想从 C# 更改 ScrollViewerListBox 的属性。

我在 Stackoverflow 上找到了 this question。我接受了已接受答案的建议,并将 ScrollViewer 暴露为子类的属性。但是,这在下面显示的示例中似乎不起作用。该问题中的一些 cmets 还表示该技术无效。

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);
        }
    }
}

当我单击“检查 ScrollViewer”按钮时,它会打印“null”。即,ScrollViewer 未被检索到。

我怎么去那个该死的ScrollViewer? :-)

【问题讨论】:

  • ...您真的不应该将您的 ScrollViewer-Property 称为“ScrollViewer”。
  • @chiffre:为什么不呢?它实际上在 .NET 属性命名指南中:考虑为属性赋予与其类型相同的名称。 (msdn.microsoft.com/en-us/library/ms229012.aspx)
  • @AvnerShahar-Kashtan:引用的链接是关于枚举的。并且仅枚举。你可能会争论这是否是一个好习惯。这只是我的观点,为类型和属性使用相同的名称会让我发疯......
  • 我知道它只明确提到了枚举,但我觉得这个想法对其他类型也有好处。我想不出一个比 ScrollViewer 更好的 ListBox 的 ScrollViewer 名称。你可以把它扭来扭去,叫它 ScrollContainer 或 ScrollProvider 或 ScrollPane,但你并没有让它更清楚。

标签: c# wpf xaml listbox scrollviewer


【解决方案1】:

你可以试试这个小助手功能

用法

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;
}

希望对你有帮助

【讨论】:

  • 哇,这是一个很好的解决方案@punker76。它不需要我继承ListBox 只是为了得到ScrollViewer。我很想将此标记为已接受的答案。欢迎讨论! :-)
  • 伟大的主题无关版本。我提供了一种稍作修改的作为具有类型安全性的扩展方法。 (我知道这个话题很老了)
  • public static TDescendant GetDescendant&lt;TDescendant&gt;(this Visual element) 在这里看起来很漂亮;D
【解决方案2】:

如果您将使用标准 ListBox,那么您可以将您的 getter 更改为这个:

public class MyListBox : ListBox
{
    public ScrollViewer ScrollViewer
    {
        get 
        {
            Border border = (Border)VisualTreeHelper.GetChild(this, 0);

            return (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
        }
    }
}

【讨论】:

  • ...您真的不应该将您的 ScrollViewer-Property 称为“ScrollViewer”。
  • 我希望@dharmatech 也能在此评论中看到它。但是:“完成。” ;)
  • @stukselbax:看起来您需要在 get 子句周围加上大括号?
  • 以属性的类型命名并没有错。其实推荐做法:msdn.microsoft.com/en-us/library/ms229012.aspx.
【解决方案3】:

我已经修改了@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))

【讨论】:

    【解决方案4】:

    对我来说,将 ScrollViewer 作为属性公开是个坏主意。首先,不能保证 ScrollViewer 存在于模板中。其次,ScrollViewer 与 ItemsPanel 和 ItemContainerGenerator 同步工作。覆盖这是不常见行为的直接方法。

    WPF 控件使用另一种模式。它们的类就像外部逻辑用法和内部视觉表示之间的中介。您的 ListBox 应该公开 ScrollViewer 可以在模板中使用的属性,但不能公开 ScrollViewer。这样一来,您就破坏了 WPF 标准,将您的控件限制在特定模板上,并允许用户代码破解内部 ListBox 实现。

    【讨论】:

      【解决方案5】:

      ScrollViewer 的属性“附加”到 ListBox(参见https://docs.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);

      【讨论】:

      • 我说是的!使用附加属性的 WPF 功能,干净。谢谢。
      【解决方案6】:

      这是@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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多