简体   繁体   中英

WPF DataGridTemplateColumn MVVM DataContext Property Accessibility Issue

I am trying to get access to a Boolean property value inside each row so I can use it to set a button visibility, however I am having trouble accessing this with a DataGridTemplateColumn. I was able to get the entire row object into a parameter that I pass to the button command, however I can't get just the UseSetting value to pass to the Visibility converter. I tried piggy backing off the text column as shown below, however the converters only seem to fire when the view is first loaded. Using breakpoints I can see that subsequent changes to the UseSetting property do not fire the converters. I do have NotifyOfPropertyChange setup correctly on the custom class used in the DataGrid.

What is the best way to gain access to a row property when using DataGridTemplateColumn? The reason why I am creating my own check boxes inside a DataGridTemplateColumn instead of using a CheckboxColumn is because the CheckboxColumn requires the row to be selected before it can be checked, and I need my checkbox to check upon a single click.

To be clear, there is no code behind for this view. Everything is in the view model, like the data grid's item source which is an ObservableCollection of the custom class "SharedSetting" that I included below.

<DataGrid MaxHeight="400" VerticalScrollBarVisibility="Auto" BorderThickness="1" CanUserAddRows="False" CanUserDeleteRows="False" BorderBrush="{DynamicResource AccentBaseColorBrush}" GridLinesVisibility="Horizontal"  AutoGenerateColumns="False"  ItemsSource="{Binding SharedSettings, NotifyOnSourceUpdated=True}">
<DataGrid.ColumnHeaderStyle>
    <Style TargetType="{x:Type DataGridColumnHeader}" >
        <Setter Property="FontWeight"  Value="Bold" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="FontSize" Value="10" />
    </Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
    <DataGridTextColumn Width="250" Header="Setting" Binding="{Binding Setting}" />
    <DataGridTextColumn Width="300" Header="Value" ElementStyle="{StaticResource WrapText}" Binding="{Binding Value}" />
    <DataGridTextColumn Width="75" Header="Use Setting" Binding="{Binding UseSetting, Mode=TwoWay}" x:Name="stackRowUseSetting" />
    <DataGridTemplateColumn Width="50" >
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Grid Width="30" Height="30" x:Name="stackRow2">
                    <Button Background="Transparent" Foreground="{StaticResource AccentColorBrush}" ToolTip="Do Not Use Setting"  Visibility="{Binding ElementName=stackRowUseSetting, Path=Binding, Converter={StaticResource TrueToVisibleConverter}}"  BorderThickness="0" Margin="0,0,0,0" DataContext="{Binding ElementName=MainGrid, Path=DataContext}" Command="{Binding ToggleUseSettingCommand}" CommandParameter="{Binding ElementName=stackRow2,Path=DataContext}">
                        <iconPacks:PackIconMaterial Kind="CheckCircleOutline" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0"    />
                    </Button>
                    <Button Background="Transparent" Foreground="{StaticResource AccentColorBrush}" ToolTip="Use Setting" Visibility="{Binding ElementName=stackRowUseSetting, Path=Binding, Converter={StaticResource FalseToVisibleConverter}}" BorderThickness="0" Margin="0,0,0,0" DataContext="{Binding ElementName=MainGrid, Path=DataContext}" Command="{Binding ToggleUseSettingCommand}" CommandParameter="{Binding ElementName=stackRow2,Path=DataContext}">
                        <iconPacks:PackIconMaterial Kind="CheckboxBlankCircleOutline" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0"    />
                    </Button>

                </Grid>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>


</DataGrid.Columns>
</DataGrid>

Also, the above XAML is just what I have right now. There are most likely some items here that are redundant and not needed. I have added and removed so many things trying to get this to work that it's a bit sloppy at the moment.

Here is the SharedSetting class with INotifyPropertyChanged

public class SharedSetting : INotifyPropertyChanged
{
    private bool _useSetting;
    private object _o;
    private string _value;
    private string _setting;
    private string _group;

    public SharedSetting(string groupName, string settingName, string settingValue, object value, bool use=false)
    {
        Group = groupName;
        Setting = settingName;
        Value = settingValue;
        Object = value;
        UseSetting = use;
    }
    public SharedSetting()
    {

    }

    public string Group
    {
        get { return _group; }
        set
        {
            _group = value;
            NotifyPropertyChanged();
        }
    }

    public string Setting
    {
        get { return _setting; }
        set
        {
            _setting = value;
            NotifyPropertyChanged();
        }
    }

    public string Value
    {
        get { return _value; }
        set
        {
            _value = value;
            NotifyPropertyChanged();
        }
    }

    public object Object
    {
        get { return _o; }
        set
        {
            _o = value;
            NotifyPropertyChanged();
        }
    }

    public bool UseSetting
    {
        get { return _useSetting; }
        set
        {
            _useSetting = value;
            NotifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Here is one of the converters.

public sealed class TrueToVisibleConverter : MarkupExtension, IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var flag = false;
        if (value is bool)
        {
            flag = (bool) value;
        }
        var visibility = (object) (Visibility) (flag ? 0 : 2);
        return visibility;
    }
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is Visibility)
        {
            var visibility = (object) ((Visibility) value == Visibility.Visible);
            return visibility;
        }
        return (object) false;
    }
}

UPDATE 3/14/18

To address the first answer supplied below regarding removing my DataContext setting and using the properties just like all of the other columns, that does not work. That was the first thing I tried long long ago only to learn that DataGridTemplateColumn doesn't inherit the row's data context like the other columns do (the reason for my frustration in my below comment yesterday). I've included a screenshot showing the intellisense error stating that the property doesn't exist, when it is used the same way as the column above it.

显示它并不像使用DataGridTextColumn一样简单

You overright DataContext for your Button . DataContext="{Binding ElementName=MainGrid, Path=DataContext}" is wrong, so delete it and bind to the property as you do it in DataGridTextColumn . And for the binding of Command to the command which is not in SharedSetting use ElementName (as you have done it for DataContext ) or RelativeSource .
Update:
Should work, but alternatively you can try

<Button Visibility="{Binding DataContext.UseSetting, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}, Converter={StaticResource TrueToVisibleConverter}}" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM