Видимість прив'язки для DataGridColumn у WPF


76

Як я можу приховати стовпець у WPF DataGridза допомогою прив'язки?

Це те, що я зробив:

<DataGridTextColumn Header="Column header"
                    Binding="{Binding ColumnValue}"
                    Width="100"
                    ElementStyle="{StaticResource DataGridRightAlign}"
                    Visibility="{Binding MyColumnVisibility}" />

І ось що я отримав (крім стовпця, який все ще видно):

Помилка System.Windows.Data: 2: Не вдається знайти керування FrameworkElement або FrameworkContentElement для цільового елемента. BindingExpression: Path = MyColumnVisibility; DataItem = null; цільовим елементом є 'DataGridTextColumn' (HashCode = 1460142); цільовою властивістю є "Видимість" (тип "Видимість")

Як виправити прив'язку?

Відповіді:


176

По-перше, DataGridTextColumnабо будь-які інші підтримувані стовпці dataGrid не містяться у візуальному деревіDataGrid . Отже, за замовчуванням він не буде наслідувати DataContextвідDataGrid . Але це працює Bindingлише для DP, а не для інших DP на DataGridColumn.

Оскільки вони не лежать в одному VisualTree, тому будь-яка спроба отримати DataContext за допомогою RelativeSourceне буде працювати, тому що DataGrid не зможе перейти до DataGrid.

Є два шляхи досягнення цього, хоча:


Спочатку використовуючи Freezableклас - Freezableоб’єкти можуть успадкувати DataContext, навіть коли вони не знаходяться у візуальному або логічному дереві. Отже, ми можемо скористатися цим для нашого використання.

Спочатку створіть клас, що успадковує від Freezableта DataDP, який ми можемо використовувати для прив'язки в XAML:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
                                     typeof(BindingProxy));
}

Тепер додайте його екземпляр у ресурси DataGrid, щоб він міг успадкувати DataContext DataGrid, а потім зв’язати зі своїм DP даних:

    <DataGrid>
        <DataGrid.Resources>
            <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
                                                Source={StaticResource proxy}}"/>
        </DataGrid.Columns>
    </DataGrid>

По-друге , ви можете посилатися на будь-який елемент інтерфейсу в XAML, використовуючи ElementNameабо x:Reference. Але ElementNameпрацює лише в одному візуальному дереві, тоді як x: Reference не має такого обмеження.

Отже, ми можемо використати це також на свою користь. Створити манекен FrameworkElementу XAML із встановленим значенням видимості для згорнутого. FrameworkElement успадкує DataContext від батьківського контейнера, який може бути Window або UserControl.

І можна використовувати це в DataGrid:

    <FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Test"
                                Binding="{Binding Name}"
                                Visibility="{Binding DataContext.IsEnable,
                                          Source={x:Reference dummyElement}}"/>
        </DataGrid.Columns>
    </DataGrid>

3
Мені подобається цей другий підхід. Це легко писати, і я вже маю ще один елемент керування такою ж видимістю, тому я можу просто дати це x:Nameпосилання та посилання на його Visibilityвластивість. Насправді не прямо-прямо, більше повертаючись убік по дорозі, але все-таки просто. Думаю, під час прив’язки до властивості DataContext елемента, на який посилається, ви «викрадаєте» інший елемент, щоб поділитися його DataContext з недосяжним інакше DataGridColumn, так? DummyElement - це лише міст.
ygoe

2
@LonelyPixel - Так, ти правильно зрозумів. Я намагаюся викрасти DataContext у його побратима DataGrid, оскільки вони обидва мають однаковий DataContext, якщо це не встановлено явно. Я міг би використовувати x: посилання з самим DataGrid, але це призвело б до циклічної залежності.
Rohit Vats

1
+1 за вашу відповідь. Вибачте, я неправильно зрозумів питання. Приблизно щодо використання x:Reference- у WPF 4.0, принаймні для Visual Studio 2010 все ще може з'являтися виняток:, Service provider is missing the INameResolver serviceйого можна ігнорувати. І, наскільки я розумію, це було виправлено у WPF 4.5.
Анатолій Миколаїв

2
Особисто, якщо ви запитаєте мене, мені подобається перший підхід. Накладні витрати - це просто створити клас, але як тільки ви маєте його у своєму кошеняті, життя стає набагато легшим для кодування в XAML. Я використовую його частіше.
Rohit Vats

2
@JMIII Не знаю, зараз я це ніде не використовую. Крім того, мені байдуже, що розуміє редактор XAML (це не так багато), поки він працює в кінці.
ygoe

18
<Window.Resources>
    <ResourceDictionary>
        <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
    </ResourceDictionary>
</Window.Resources>

<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
                  AutoGenerateColumns="False"
                  FlowDirection="LeftToRight"
                  ItemsSource="{Binding PayStructures}"
                  SelectedItem="{Binding SelectedItem}">
    <DataGrid.Columns>
         <DataGridTemplateColumn Width="70"
                                 Header="name"
                                 IsReadOnly="True"
                                 Visibility="{Binding DataContext.IsShowName,
                                 Source={StaticResource ProxyElement}}">
             <DataGridTemplateColumn.CellTemplate>
                 <DataTemplate>
                     <TextBlock Text="{Binding FieldName}" />
                 </DataTemplate>
             </DataGridTemplateColumn.CellTemplate>
         </DataGridTemplateColumn>                   
     </DataGrid.Columns>
</mch:MCHDataGrid>

Зразок пов'язаної властивості у моделі подання:

private Visibility _isShowName;

public Visibility IsShowName
{
    get { return _isShowName; }
    set
    {
        _isShowName = value;
        OnPropertyChanged();
    }
}

Я думаю, це вже було запропоновано рік тому. Запізно.
ygoe

Якщо ви хочете надрукувати клас поточного DataContext, використовуйте це:<TextBlock Text="{Binding DataContext, Source={StaticResource ProxyElement}}"></TextBlock>
Contango

Не працює, якщо контекст даних насправді не є статичним, але може відрізнятися. У цьому випадку я отримую: "Помилка System.Windows.Data: 3: Не вдається знайти елемент, що забезпечує DataContext. BindingExpression: (без шляху); DataItem = null; цільовим елементом є" FrameworkElement "(Name =" ProxyFrameworkElement "); target властивістю є "DataContext" (тип "Об'єкт") "при створенні вікна.
JS
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.