Вопрос по silverlight – Как связать DataGridColumn.Visibility?

15

У меня есть проблема, похожая на следующий пост:

Видимость привязки Silverlight DataGridTextColumn

Мне нужно, чтобы столбец в DataGrid Silverlight был видимым / свернутым на основе значения в ViewModel. Для этого я пытаюсь привязать свойство Visibility к ViewModel. Однако вскоре я обнаружил, что свойство Visibility не является DependencyProperty, поэтому оно не может быть связано.

Чтобы решить эту проблему, я попытался создать подкласс своего собственного DataGridTextColumn. С помощью этого нового класса я создал объект DependencyProperty, который в конечном итоге вносит изменения в свойство DataGridTextColumn.Visibility. Это хорошо работает, если я не привязываю данные. В тот момент, когда я связываю данные с моим новым свойством, оно перестает работать, за исключением AG_E_PARSER_BAD_PROPERTY_VALUE.

<code>public class MyDataGridTextColumn : DataGridTextColumn
{
    #region public Visibility MyVisibility

    public static readonly DependencyProperty MyVisibilityProperty =
        DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(MyDataGridTextColumn), new PropertyMetadata(Visibility.Visible, OnMyVisibilityPropertyChanged));

    private static void OnMyVisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var @this = d as MyDataGridTextColumn;

        if (@this != null)
        {
            @this.OnMyVisibilityChanged((Visibility)e.OldValue, (Visibility)e.NewValue);
        }
    }

    private void OnMyVisibilityChanged(Visibility oldValue, Visibility newValue)
    {
        Visibility = newValue;
    }

    public Visibility MyVisibility
    {
        get { return (Visibility)GetValue(MyVisibilityProperty); }
        set { SetValue(MyVisibilityProperty, value); }
    }

    #endregion public Visibility MyVisibility
}
</code>

Вот небольшой фрагмент XAML.

<code><DataGrid ....>
    <DataGrid.Columns>
        <MyDataGridTextColumn Header="User Name"
                              Foreground="#FFFFFFFF"
                              Binding="{Binding User.UserName}"
                              MinWidth="150"
                              CanUserSort="True"
                              CanUserResize="False"
                              CanUserReorder="True"
                              MyVisibility="{Binding Converter={StaticResource BoolToVisibilityConverter}, Path=ShouldShowUser}"/>
        <DataGridTextColumn .../>
    </DataGrid.Columns>
</DataGrid>
</code>

A couple important facts.

The Converter is indeed defined above in the local resources. The Converter is correct, it is used many other places in the solution. If I replace the {Binding} syntax for the MyVisibility property with "Collapsed" the Column does in fact disappear. If I create a new DependencyProperty (i.e. string Foo), and bind to it I receive the AG_E_PARSER_BAD_PROPERTY_VALUE exception too.

У кого-нибудь есть идеи относительно того, почему это не работает?

Любое решение по этому поводу? Я не уверен, что происходит (dp выглядит нормально для меня ...), но если вы не можете добавить новую строку DependencyProperty и связать ее, кажется, что это проблема. Erik Mork

Ваш Ответ

9   ответов
1

вы не создаете привязку к & quot; Binding & quot; свойство столбца сетки данных. Хорошо, вы пишете «{Binding User.UserName}», но он не создает привязку, потому что (как сказал Захари) столбец сетки данных не наследуется от FrameworkElement и не имеет метода SetBinding. Так что выражение & quot; {Binding User.UserName} & quot; просто создает объект Binding и присваивает его свойству Binding столбца (это свойство типа Binding). Затем столбец таблицы данных при создании содержимого ячеек (метод GenerateElement - защищенный) использует этот объект Binding для установки привязки к сгенерированным элементам (например, к свойству Text сгенерированного TextBlock), которые являются FrameworkElements

7

Во-первых, вам нужно наследовать от DataGrid.

public class DataGridEx : DataGrid
{
    public IEnumerable<string> HiddenColumns
    {
        get { return (IEnumerable<string>)GetValue(HiddenColumnsProperty); }
        set { SetValue(HiddenColumnsProperty, value); }
    }

    public static readonly DependencyProperty HiddenColumnsProperty =
        DependencyProperty.Register ("HiddenColumns", 
                                     typeof (IEnumerable<string>), 
                                     typeof (DataGridEx),
                                     new PropertyMetadata (HiddenColumnsChanged));

    private static void HiddenColumnsChanged(object sender,
                                             DependencyPropertyChangedEventArgs args)
    {
        var dg = sender as DataGrid;
        if (dg==null || args.NewValue == args.OldValue)
            return;

        var hiddenColumns = (IEnumerable<string>)args.NewValue;
        foreach (var column in dg.Columns)
        {
            if (hiddenColumns.Contains ((string)column.GetValue (NameProperty)))
                column.Visibility = Visibility.Collapsed;
            else
                column.Visibility = Visibility.Visible;
        }
    }
}

DataGridEx Класс добавляет новый DP для скрытия столбцов на основеx:Name изDataGridColumn и его потомки.

Для использования в вашем XAML:

<my:DataGridEx x:Name="uiData"
               DataContext="{Binding SomeDataContextFromTheVM}"
               ItemsSource="{Binding Whatever}"
               HiddenColumns="{Binding HiddenColumns}">
    <sdk:DataGridTextColumn x:Name="uiDataCountOfItems">
                            Header="Count"
                            Binding={Binding CountOfItems}"
    </sdk:DataGridTextColumn>
</my:DataGridEx>

Вам необходимо добавить их в вашу ViewModel или любой другой контекст данных, который вы используете.

private IEnumerable<string> _hiddenColumns;
public IEnumerable<string> HiddenColumns
{
    get { return _hiddenColumns; }
    private set
    {
        if (value == _hiddenColumns)
            return;

        _hiddenColumns = value;
        PropertyChanged (this, new PropertyChangedEventArgs("HiddenColumns"));
    }
}

public void SomeWhereInYourCode ()
{
    HiddenColumns = new List<string> {"uiDataCountOfItems"};
}

Чтобы отобразить, вам нужно только удалить соответствующее имя из списка или воссоздать его без скрытого имени.

Прекрасно работает с Silverlight .. :) Большое спасибо человек, вы сделали мой день .. !!
Спасибо ... Я еще не реализовал это, однако похоже, что он может сработать! Chris Mancini
6

в котором используется подход, аналогичный «Binding». свойство, которое вы найдете на DataGridTextColumn. Поскольку классы столбцов являются DependencyObjects, вы не можете напрямую привязать к ним данные, НО, если вы добавите ссылку на FrameworkElement, который реализует INotifyPropertyChanged, вы можете передать привязку данных элементу, а затем использовать свойство зависимости, чтобы уведомить Столбец о том, что привязка данных изменилась.

Следует отметить, что наличие привязки к самому столбцу вместо Grid, вероятно, будет означать, что вы захотите использоватьDataContextProxy чтобы получить доступ к полю, с которым вы хотите связать видимость (привязка столбца по умолчанию будет соответствовать области действия 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"));
            }
        }
    }
}

}

Наконец-то решение, которое действительно работает!
Однако мне пришлось закомментировать вторую последнюю строку (n.MyVisibility = ...), чтобы последующие переключатели работали.
Привет, это не работает для моего случая. Неудовлетворительно.
3

lement. В WPF это не составило бы труда ... но в silverlight вы можете связывать только объекты FrameworkElement. Таким образом, при попытке получить сообщение об ошибке AG_E_PARSER_BAD_PROPERTY_VALUE.

Это не так. Silverlight позволяет связывать со свойствами зависимостей DependencyObjects. Проблема только в том, что свойство Visibility столбцов не является DependencyProperties.
Спасибо зачары. Если то, что вы описываете, является ситуацией, можете ли вы описать, почему привязка к & quot; Binding & quot; собственность работает? Chris Mancini
извини ... Хотелось бы знать, но на этом мои познания в Silverlight заканчиваются.
0

taGrid. Затем вы извлекаете вашу ViewModel из DataContext из DataGrid и добавляете обработчик в событие PropertyChanged вашей ViewModel. В обработчике вы просто проверяете имя свойства и его значение и соответственно изменяете видимость столбца. Это не совсем лучшее решение, но оно должно работать;)

1

но его нужно немного изменить, чтобы оно заработало.

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

насколько это поможет, но я столкнулся с проблемой отсутствия свойства зависимости со столбцами сетки данных в моем последнем проекте. Чтобы обойти это, я создал событие в модели представления столбцов сетки, затем, когда сетка собирается в клиенте, используйте замыкание, чтобы подписать столбец сетки на модель представления столбцов. Моя конкретная проблема была вокруг ширины. Он начинается с класса модели представления для столбца сетки, который выглядит примерно так: псевдокод:

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

Тогда есть код, который собирает сетку:

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.
        f,oreach(var column in viewModel.Columns)
        {
            var gridCol = new DataGridTextColumn( ... );
            column.ColumnResized  += delegate(double width) { gridCol.Width = new DataGridLength(width); };
        }
    }
}

При изменении размера сетки данных в приложении происходит событие resize, которое вызывает метод resize в модели представления, к которой привязана сетка. Это, в свою очередь, вызывает метод изменения размера каждой модели представления столбца сетки. Модель представления столбца сетки затем поднимаетColumnResized событие, на которое подписан текстовый столбец сетки данных, и его ширина обновлена.

Я понимаю, что это не решает вашу проблему напрямую, но это был способ, которым я мог "связать" модель представления для столбца сетки данных, когда для него нет свойств зависимости. Закрытие - это простая конструкция, которая прекрасно описывает желаемое поведение и вполне понятна для тех, кто идет за мной. Я думаю, нетрудно представить, как его можно изменить, чтобы справиться с изменением видимости. Вы даже можете подключить обработчик событий к событию загрузки страницы / пользовательского элемента управления.

1

что проблема не так проста, как "Видимость". не являясь свойством зависимости. В DataGrid столбцы не являются частью визуального «дерева». поэтому вы не можете использовать AncestorType даже в WPF (или Silverlight 5).

Вот пара ссылок, связанных с WPF (пожалуйста, прокомментируйте, работает ли какая-либо из них для Silverlight - извините, у меня нет времени на тестирование сейчас)

Имеет действительно хорошее объяснение проблемы и сбоев определенных решений (и умное решение): http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

И пара вопросов StackOverflow:

WPF Hide DataGridColumn через привязку

Привязка Видимое свойство DataGridColumn в WPF DataGrid

1

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

Похожие вопросы