简体   繁体   中英

WPF Context Menu pops up after Binding changes

I have a Border control inside a UserControl which is used as a switch for graphical changes. The Border has a context menu with two MenuItems: "Open" and "Close".

When the switch is closed only the "Open" MenuItem is visible, and when the switch is open only "Close" is visible. For some switches I need to completely disable Open or Close, so I don't want them to be visible at any time. Here is the code:

<Border.ContextMenu>
    <ContextMenu Name="switchContextMenu">
        <ContextMenu.Visibility>
            <MultiBinding Converter="{StaticResource ContextMenuBoolToVisibility}">
                <Binding Path="OpenAvailable" />
                <Binding Path="OpenVisible" />
                <Binding Path="CloseAvailable" />
                <Binding Path="CloseVisible" />
            </MultiBinding>
        </ContextMenu.Visibility>
        <MenuItem Name="miOpen" Header="{Binding Path=Resources.PowerControlSystem_OPEN, Source={StaticResource LocalizedStrings} }" 
                  Click="miOpen_Click">
            <MenuItem.Visibility>
                <MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
                    <Binding Path="OpenAvailable" />
                    <Binding Path="OpenVisible" />
                </MultiBinding>
            </MenuItem.Visibility>
        </MenuItem>
        <MenuItem Name="miClose" Header="{Binding Path=Resources.PowerControlSystem_CLOSE, Source={StaticResource LocalizedStrings} }" 
                  Click="miClose_Click">
            <MenuItem.Visibility>
                <MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
                    <Binding Path="CloseAvailable" />
                    <Binding Path="CloseVisible" />
                </MultiBinding>
            </MenuItem.Visibility>
        </MenuItem>
    </ContextMenu>
</Border.ContextMenu>

I use this switch as a generic component, so inside another .xaml code I set the "Open Available" and "Close Available" properties. Here is my problem:

I have a switch with the Open Available property set to False. By default, this switch is open. I can closed it, and that's OK. Then when I right click on this switch again the context menu is not visible. This is also OK.

The problem is when I change some values this switch opens again, and at that point of time, the Context Menu pops up. I think that the problem is in the Converter for the ContextMenu visibility. It is triggered at this point and the ContextMenu becomes visible.

Does anyone have an idea how to stop this behavior?

Instead of setting the ContextMenu visibility, you can style the Border

<Border>
    <Border.Style>
        <Style TargetType="Border">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu Name="switchContextMenu">
                        <MenuItem Name="miOpen" Header="{Binding Path=Resources.PowerControlSystem_OPEN, Source={StaticResource LocalizedStrings} }" 
                            Click="miOpen_Click">
                            <MenuItem.Visibility>
                                <MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
                                    <Binding Path="OpenAvailable" />
                                    <Binding Path="OpenVisible" />
                                </MultiBinding>
                            </MenuItem.Visibility>
                        </MenuItem>
                        <MenuItem Name="miClose" Header="{Binding Path=Resources.PowerControlSystem_CLOSE, Source={StaticResource LocalizedStrings} }" 
                            Click="miClose_Click">
                            <MenuItem.Visibility>
                                <MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
                                    <Binding Path="CloseAvailable" />
                                    <Binding Path="CloseVisible" />
                                </MultiBinding>
                            </MenuItem.Visibility>
                        </MenuItem>
                    </ContextMenu>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <DataTrigger Value="False">
                    <DataTrigger.Binding>
                        <MultiBinding Converter="{StaticResource ContextMenuBoolAggregate}">
                            <Binding Path="OpenAvailable" />
                            <Binding Path="OpenVisible" />
                            <Binding Path="CloseAvailable" />
                            <Binding Path="CloseVisible" />
                        </MultiBinding>
                    </DataTrigger.Binding>
                    <Setter Property="ContextMenu" Value="{x:Null}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Border.Style>
</Border>

So the trigger is conditionally removing the ContextMenu instead of changing its visibility. I used the ContextMenuBoolAggregate converter name to indicate you need to create a new converter that is determining the correct value to remove the context menu - I think it's clear how to implement that part.

If your condition is simple enough, you can use MultiDataTrigger instead of DataTrigger with MultiBinding and Converter :

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding OpenAvailable}" Value="False"/>
        <Condition Binding="{Binding OpenVisible}" Value="False"/>
        <Condition Binding="{Binding CloseAvailable}" Value="False"/>
        <Condition Binding="{Binding CloseVisible}" Value="False"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="ContextMenu" Value="{x:Null}"/>
</MultiDataTrigger>

I found solution in another way. I used ContextMenuService.IsEnabled property. When is set to false, Context Menu can't be shown. So, I added this in my Border.xaml code:

<ContextMenuService.IsEnabled>
    <MultiBinding Converter="{StaticResource ContextMenuBoolToVisibility}">
        <Binding Path="OpenAvailable" ElementName="mySwitch" />
        <Binding Path="OpenVisible" ElementName="mySwitch" />
        <Binding Path="CloseAvailable" ElementName="mySwitch" />
        <Binding Path="CloseVisible" ElementName="mySwitch" />
    </MultiBinding>
</ContextMenuService.IsEnabled>

I also changed my ContextMenuBoolToVisibility converter. Instead of returning Visibility.Visible or Visibility.Collapsed , now returns true or false for ContextMenuService.IsEnabled property.

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