How to bind DataGridColumn.Visibility?

前端 未结 9 1916
悲哀的现实
悲哀的现实 2020-12-10 03:46

I have an issue similar to the following post:

Silverlight DataGridTextColumn Binding Visibility

I need to have a Column within a Silverlight DataGrid be vis

相关标签:
9条回答
  • 2020-12-10 03:49

    The datagrid column inherits from DependencyObject instead of FrameworkElement. In WPF this would be no big deal... but in silverlight you can only bind to FrameworkElement objects. So you get the descriptive error message of AG_E_PARSER_BAD_PROPERTY_VALUE when you try.

    0 讨论(0)
  • 2020-12-10 03:50

    I have another solution to this problem that uses an approach similar to the "Binding" property that you find on DataGridTextColumn. Since the column classes are DependencyObjects, you can't directly databind to them, BUT if you add a reference to a FrameworkElement that implements INotifyPropertyChanged you can pass a databinding through to the element, and then use a dependency property to notify the Column that the databinding has changed.

    One thing to note is that having the binding on the Column itself instead of the Grid will probably mean that you will want to use a DataContextProxy to get access to the field that you want to bind the Visibility to (the column binding will default to the scope of the ItemSource).

    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace XYZ.Controls
    {
    public class ExtendedDataGridTextColumn : DataGridTextColumn
    {
        private readonly Notifier _e;
    
        private Binding _visibilityBinding;
        public Binding VisibilityBinding
        {
            get { return _visibilityBinding; }
            set
            {
                _visibilityBinding = value;
                _e.SetBinding(Notifier.MyVisibilityProperty, _visibilityBinding);
            }
        }
    
        public ExtendedDataGridTextColumn()
        {
            _e = new Notifier();
            _e.PropertyChanged += ToggleVisibility;
        }
    
        private void ToggleVisibility(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Visibility")
                this.Visibility = _e.MyVisibility;
        }
    
        //Notifier class is just used to pass the property changed event back to the column container Dependency Object, leaving it as a private inner class for now
        private class Notifier : FrameworkElement, INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public Visibility MyVisibility
            {
                get { return (Visibility)GetValue(MyVisibilityProperty); }
                private set { SetValue(MyVisibilityProperty, value); }
            }
    
            public static readonly DependencyProperty MyVisibilityProperty = DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(Notifier), new PropertyMetadata(MyVisibilityChanged));
    
            private static void MyVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var n = d as Notifier;
                if (n != null)
                {
                    n.MyVisibility = (Visibility) e.NewValue;
                    n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
                }
            }
        }
    }
    

    }

    0 讨论(0)
  • 2020-12-10 03:51

    This works on a data grid template column:

    public class ExtendedDataGridColumn : DataGridTemplateColumn
    {
        public static readonly DependencyProperty VisibilityProperty = DependencyProperty.Register("Visibility", typeof(Visibility), typeof(DataGridTemplateColumn), new PropertyMetadata(Visibility.Visible, VisibilityChanged));
        public new Visibility Visibility
        {
            get { return (Visibility)GetValue(VisibilityProperty); }
            set { SetValue(VisibilityProperty, value); }
        }
        private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ((DataGridTemplateColumn)d != null)
            {
                ((DataGridTemplateColumn)d).Visibility = (Visibility)e.NewValue;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-10 03:53

    Note that the problem isn't just as simple as 'Visibility' not being a dependency property. In a DataGrid the columns aren't part of the visual 'tree' so you can't use AncestorType even in WPF (or Silverlight 5).

    Here's a couple WPF related links (please comment if any of these work for Silverlight - sorry I don't have time to test now)

    Has a really nice explanation of the problem and failures of certain solutions (and a clever solution): http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

    And a couple StackOverflow questions:

    WPF Hide DataGridColumn via a binding

    Binding Visible property of a DataGridColumn in WPF DataGrid

    0 讨论(0)
  • 2020-12-10 03:54

    I don't know how much this will help, but I've run into the lack of dependency property problem with data grid columns myself in my latest project. What I did to get around it, was to create an event in the grid column view model, then when the grid is being assembled in the client, use a closure to subscribe the grid column to the column view model. My particular problem was around width. It starts with the view model class for the grid column, which looks something like this pseudo-code:

    public delegate void ColumnResizedEvent(double width);
    
    public class GridColumnViewModel : ViewModelBase
    {
        public event ColumnResizedEvent ColumnResized;
    
        public void Resize(double newContainerWidth)
        {
            // some crazy custom sizing calculations -- don't ask...
            ResizeColumn(newWidth);
        }
    
        public void ResizeColumn(double width)
        {
            var handler = ColumnResized;
            if (handler != null)
                handler(width);
        }
    }
    

    Then there's the code that assembles the grid:

    public class CustomGrid
    {
        public CustomGrid(GridViewModel viewModel)
        {
            // some stuff that parses control metadata out of the view model.
            // viewModel.Columns is a collection of GridColumnViewModels from above.
            foreach(var column in viewModel.Columns)
            {
                var gridCol = new DataGridTextColumn( ... );
                column.ColumnResized  += delegate(double width) { gridCol.Width = new DataGridLength(width); };
            }
        }
    }
    

    When the datagrid is resized in the application, the resize event is picked up and calls the resize method on the viewmodel the grid is bound to. This in turn calls the resize method of each grid column view model. The grid column view model then raises the ColumnResized event, which the data grid text column is subscribed to, and it's width is updated.

    I realise this isn't directly solving your problem, but it was a way I could "bind" a view model to a data grid column when there are no dependency properties on it. The closure is a simple construct that nicely encapsulates the behaviour I wanted, and is quite understandable to someone coming along behind me. I think it's not too hard to imagine how it could be modified to cope with visibility changing. You could even wire the event handler up in the load event of the page/user control.

    0 讨论(0)
  • 2020-12-10 03:54

    GreatTall1's solution is great, but it need to bit change to make it work.

    var n = d as Notifier;
    if (n != null)
    {
         //Assign value in the callback will break the binding.
         //n.MyVisibility = (Visibility)e.NewValue;
         n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
    }
    
    0 讨论(0)
提交回复
热议问题