Подтвердить что ты не робот

Доступ к ScrollViewer из ListBox из С#

Я хотел бы изменить свойства ScrollViewer a ListBox на С#.

Я нашел этот вопрос здесь, в 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>

С#:

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". I.e., ScrollViewer не был извлечен.

Как мне добраться до этого darn ScrollViewer?: -)

4b9b3361

Ответ 1

Если вы будете использовать стандартный ListBox, чтобы вы могли изменить свой getter на этот:

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

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

Ответ 2

вы можете попробовать эту небольшую вспомогательную функцию

Использование

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

Надеюсь, что это поможет

Ответ 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

Здесь другая переработанная и родовая версия @punker76 отвечает за С# 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;
    }
}

Ответ 6

Свойства 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);