2012-04-24 15 views
12

Me gustaría cambiar las propiedades de un ScrollViewer de un ListBox desde C#.Accediendo al ScrollViewer de un ListBox desde C#

Encontré this question aquí en Stackoverflow. Tomé el consejo de la respuesta aceptada y expuse el ScrollViewer como una propiedad de una subclase. Sin embargo, esto no parece estar funcionando en un ejemplo que se muestra a continuación. Algunos de los comentarios en esa pregunta también afirman que esta técnica no funcionó.

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

Al hacer clic en el botón "Comprobar ScrollViewer", se imprime "nulo". Es decir, el ScrollViewer no fue recuperado.

¿Cómo llego a ese maldito ScrollViewer? :-)

+0

Consulte igualmente http://stackoverflow.com/questions/3963341/get-reference-to-my-wpf-listboxs-scrollviewer-in-c – Klaus78

+0

... y realmente no debería llamar a su ScrollViewer-Property "ScrollViewer". – basti

+2

@chiffre: ¿por qué no? En realidad, se encuentra en las Pautas de nombres de .NET para las propiedades: ** Considere dar a una propiedad el mismo nombre que su tipo. ** (http://msdn.microsoft.com/en-us/library/ms229012.aspx) –

Respuesta

6

Si va a utilizar ListBox estándar, por lo puede cambiar su getter a este:

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

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

... y realmente no debería llamar a su ScrollViewer-Property "ScrollViewer". – basti

+0

dígalo a @dharmatech :) – stukselbax

+0

Esperaba que @dharmatech también lo viera en este comentario. Pero: "Hecho". ;) – basti

20

puede probar esta función auxiliar pequeño

uso

var scrollViewer = GetDescendantByType(yourListBox, typeof(ScrollViewer)) as ScrollViewer; 

función auxiliar

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

Esperanza esto ayuda

+0

Guau, esta es una buena solución @ punker76. No requiere que subclass 'ListBox' solo para llegar al 'ScrollViewer'. Estoy tentado de marcar esta la respuesta aceptada. Debate bienvenido! :-) – dharmatech

+0

Gran versión agnóstica de tema. Proporcioné uno ligeramente modificado como método de extensión con seguridad tipo. (Sé que este tema es antiguo) – Samuel

+0

'público estático TDescendant GetDescendant (este elemento visual)' se veía muy bien aquí; D –

1

En cuanto a mí, exponer ScrollViewer como una propiedad es una mala idea. En primer lugar, no hay garantía de que ScrollViewer exista en una plantilla. En segundo lugar, ScrollViewer funciona en sincronización con ItemsPanel y ItemContainerGenerator. Sobrescribir esta es la forma directa de comportamiento poco común.

Los controles WPF usan otro patrón. Sus clases son como mediadores entre el uso lógico externo y la representación visual interna. Su ListBox debe exponer las propiedades que ScrollViewer puede usar en una plantilla, pero no en ScrollViewer. Al hacerlo, se rompen los estándares de WPF, se restringe el control a una plantilla específica y se permite que el código de usuario piratee la implementación interna de ListBox.

4

He modificado la gran respuesta de @ punker76 para crear un método de extensión para Visual y proporcionar explícita tipo de retorno:

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

    } 

Ahora puede llamarlo por SomeVisual.GetDescendantByType y se devuelve o bien ya una correcta ScrollViewer escrita a máquina o nula (que es el predeterminado (T))

0

Aquí está otra versión reelaborada y genérica de respuesta de @ punker76 para 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; 
    } 
} 
Cuestiones relacionadas