简体   繁体   English

WPF中特定于标头的上下文菜单绑定

[英]Header-specific context menu binding in WPF

I'm trying to construct context menus for a WPF datagrid which are header-specific (in other words, each column header could have it's own context menu different from the other headers). 我正在尝试为特定于标头的WPF数据网格构建上下文菜单(换句话说,每个列标题可以使其自己的上下文菜单与其他标题不同)。 In addition, the data backing the context menu is bound data. 此外,支持上下文菜单的数据是绑定数据。 My problem is that I can't seem to connect my menu to my data context. 我的问题是我似乎无法将菜单连接到我的数据上下文。 I've tried a few suggestions from here and elsewhere but so far no luck. 我从这里和其他地方尝试过一些建议,但到目前为止还没有运气。

I know that the context menu isn't within the visual tree of the rest of the document so I've tried using it's PlacementTarget (as you can see below) but when I click on a column header, my PlacementTarget is only the TextBlock of the header. 我知道上下文菜单不在文档其余部分的可视树中,所以我尝试使用它的PlacementTarget(如下所示)但是当我点击列标题时,我的PlacementTarget只是TextBlock标题。 How do I get from there to the DataContext of the grid? 我如何从那里到达网格的DataContext?

This is an example of what I've tried: 这是我尝试过的一个例子:

<Window x:Class="ContextMenuExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ContextMenuExample"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:ExampleViewModel />
</Window.DataContext>
<Grid>
    <DataGrid ItemsSource="{Binding MyGridData}" IsReadOnly="True" AutoGenerateColumns="False">
        <!-- THESE WORK, BUT THEY ARE GRID-GLOBAL.  I WANT HEADER-SPECIFIC CONTEXT MENUS -->
        <DataGrid.ContextMenu>
            <ContextMenu>
                <CheckBox  Name="GridCheckbox1" Content="Grid Menu - Item1" IsChecked="{Binding Column1Checked, Mode=TwoWay}"/>
                <CheckBox  Name="GridCheckbox2" Content="Grid Menu - Item2" IsChecked="{Binding Column2Checked, Mode=TwoWay}"/>
                <CheckBox  Name="GridCheckbox3" Content="Grid Menu - Item3" IsChecked="{Binding Column3Checked, Mode=TwoWay}"/>
            </ContextMenu>
        </DataGrid.ContextMenu>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Column 1 Data" Binding="{Binding Column1Data}" Width="Auto">
                <!-- ATTEMPT 1 - JUST SEE IF I CAN BIND DIRECTLY.  SHOULDN'T WORK. -->
                <!-- System.Windows.Data Error: 40 : BindingExpression path error: 'Column1Checked' property not found on 'object' ''String' (HashCode=-1586790989)'. BindingExpression:Path=Column1Checked; DataItem='String' (HashCode=-1586790989); target element is 'CheckBox' (Name='Header1Checkbox'); target property is 'IsChecked' (type 'Nullable`1') -->
                <DataGridTextColumn.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Name="Header1TextBlock" Text="{TemplateBinding Content}" >
                            <TextBlock.ContextMenu>
                                <ContextMenu>
                                    <CheckBox Name="Header1Checkbox" Content="Header Menu 1" IsChecked="{Binding Column1Checked, Mode=TwoWay}"/>
                                </ContextMenu>
                            </TextBlock.ContextMenu>
                        </TextBlock>
                    </DataTemplate>
                </DataGridTextColumn.HeaderTemplate>
            </DataGridTextColumn>
            <DataGridTextColumn Header="Column 2 Data" Binding="{Binding Column1Data}" Width="Auto">
                <!-- ATTEMPT 2 - BIND TO THE PLACEMENT TARGET. THIS MAKES THE TextBlock MY DATA CONTEXT.  CLOSER (?) BUT STILL WRONG -->
                <!-- System.Windows.Data Error: 40 : BindingExpression path error: 'Column2Checked' property not found on 'object' ''TextBlock' (Name='Header2TextBlock')'. BindingExpression:Path=Column2Checked; DataItem='TextBlock' (Name='Header2TextBlock'); target element is 'CheckBox' (Name='Header2Checkbox'); target property is 'IsChecked' (type 'Nullable`1') -->
                <DataGridTextColumn.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Name="Header2TextBlock" Text="{TemplateBinding Content}" >
                            <TextBlock.ContextMenu>
                                <ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
                                    <CheckBox Name="Header2Checkbox" Content="Header Menu 2" IsChecked="{Binding Column2Checked, Mode=TwoWay}"/>
                                </ContextMenu>
                            </TextBlock.ContextMenu>
                        </TextBlock>
                    </DataTemplate>
                </DataGridTextColumn.HeaderTemplate>
            </DataGridTextColumn>
            <DataGridTextColumn Header="Column 3 Data" Binding="{Binding Column1Data}" Width="Auto">
                <!-- ATTEMPT 3 - BIND TO THE PLACEMENT TARGETS' CONTEXT. I'M NOT EVEN SURE WHAT THIS IS, BUT IT DOESN'T WORK -->
                <!-- System.Windows.Data Error: 40 : BindingExpression path error: 'Column3Checked' property not found on 'object' ''String' (HashCode=975011251)'. BindingExpression:Path=Column3Checked; DataItem='String' (HashCode=975011251); target element is 'CheckBox' (Name='Header3Checkbox'); target property is 'IsChecked' (type 'Nullable`1') -->
                <DataGridTextColumn.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Name="Header3TextBlock" Text="{TemplateBinding Content}" >
                            <TextBlock.ContextMenu>
                                <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                                    <CheckBox Name="Header3Checkbox" Content="Header Menu 3" IsChecked="{Binding Column3Checked, Mode=TwoWay}"/>
                                </ContextMenu>
                            </TextBlock.ContextMenu>
                        </TextBlock>
                    </DataTemplate>
                </DataGridTextColumn.HeaderTemplate>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

class GridData
{
    public int Column1Data { get; set; }
    public int Column2Data { get; set; }
    public int Column3Data { get; set; }
}

class ExampleViewModel : INotifyPropertyChanged
{
    private bool column1Checked;
    private bool column2Checked;
    private bool column3Checked;

    public event PropertyChangedEventHandler PropertyChanged;

    public List<GridData> MyGridData
    {
        get
        {
            return new List<GridData>
            {
                new GridData() { Column1Data = 1, Column2Data = 2, Column3Data = 3},
                new GridData() { Column1Data = 4, Column2Data = 5, Column3Data = 6},
                new GridData() { Column1Data = 7, Column2Data = 8, Column3Data = 9}
            };
        }
    }

    public bool Column1Checked
    { 
        get { return column1Checked; }
        set {
            if (column1Checked != value)
            {
                column1Checked = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Column1Checked)));
            }
        }
    }
    public bool Column2Checked
    { 
        get { return column2Checked; }
        set {
            if (column2Checked != value)
            {
                column2Checked = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Column2Checked)));
            }
        }
    }
    public bool Column3Checked
    { 
        get { return column3Checked; }
        set {
            if (column3Checked != value)
            {
                column3Checked = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Column3Checked)));
            }
        }
    }
}

So my question is... How can I bind my context menus to my ViewModel? 所以我的问题是......如何将我的上下文菜单绑定到我的ViewModel?

Thanks for any help you can offer. 谢谢你的尽心帮助。

Try this. 尝试这个。

<DataGridTextColumn Header="Column 1 Data" Binding="{Binding Column1Data}" Width="Auto">
    <DataGridTextColumn.HeaderTemplate>
        <DataTemplate>
            <TextBlock Name="Header1TextBlock" Text="{TemplateBinding Content}">
                <TextBlock.ContextMenu>
                    <ContextMenu>
                        <CheckBox Name="Header1Checkbox" Content="Header Menu 1" IsChecked="{Binding DataContext.Column1Checked, RelativeSource={RelativeSource AncestorType=Window}, Mode=TwoWay}"/>
                    </ContextMenu>
                </TextBlock.ContextMenu>
            </TextBlock>
        </DataTemplate>
    </DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>

I am not too sure if it works because your ContextMenu is defined inside a Template. 我不太确定它是否有效,因为您的ContextMenu是在模板中定义的。

I finally found what I needed here: 我终于找到了我需要的东西:
WPF ContextMenu woes: How do I set the DataContext of the ContextMenu? WPF ContextMenu困境:如何设置ContextMenu的DataContext?
Thanks to daub815 感谢daub815

This is what worked for me: 这对我有用:

                <DataGridTextColumn Header="Column 1 Data" Binding="{Binding Column1Data}" Width="Auto">
                <DataGridTextColumn.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Name="Header1TextBlock" Text="{TemplateBinding Content}" Tag="{Binding DataContext, ElementName=MyDataGrid}">
                            <TextBlock.ContextMenu>
                                <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                                    <CheckBox Name="Header1Checkbox" Content="Header Menu 1" IsChecked="{Binding Column1Checked, Mode=TwoWay}"/>
                                </ContextMenu>
                            </TextBlock.ContextMenu>
                        </TextBlock>
                    </DataTemplate>
                </DataGridTextColumn.HeaderTemplate>
            </DataGridTextColumn>

(Where MyDataGrid is the DataGrid object) (MyDataGrid是DataGrid对象的位置)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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