Frage

I'm developing a small application in WPF with Caliburn and while mostly it's been a joy there is one exception. ContextMenus. After much searching, learning and reading on the subject here on SO and elsewhere I've realized ContextMenus are more or less broken in both WPF and Caliburn, but I can't avoid them in this project.

My problem is, as for so many others it seems, a matter of scope and visual trees, but it's a little different. I have finally gotten ContextMenus to properly bubble actions except for the "ContextMenu.Closed" event. My Code:

View:

<UserControl x:Class="WPFClient.Plugins.IndividualDataView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
         DataContext="{Binding RelativeSource={RelativeSource Self}}"
         x:Name="IndividualQueue">

<ItemsControl x:Name="Channels">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
    <Grid HorizontalAlignment="Center" Tag="{Binding DataContext, ElementName=IndividualQueue}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ContextMenu>
            <ContextMenu Name="individualcm"
            cal:Message.Attach="[Event Opened] = [Action FreezeUpdates()];[Event Closed] = [Action ResumeUpdates()]"
            cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                <MenuItem Header="{Binding Initials}" cal:Message.Attach="[Event Click] = [Action SetInactive()]" />
            </ContextMenu>
        </Grid.ContextMenu>
        <Image Height="24" Width="24" Margin="3, 3, 3, 3" HorizontalAlignment="Left" Source="{Binding urlImage}" Grid.Row="0" />
        <TextBlock Name="Initials" Text="{Binding Initials}" Grid.Row="1" HorizontalAlignment="Center" />
    </Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>

ViewModel:

public void FreezeUpdates()
{
    _events.Unsubscribe(this);
}
public void ResumeUpdates()
{
    _events.Subscribe(this);
}

The point is that in order to avoid the ItemsControl from updating while the ContextMenu is open due to an incoming event (which will corrupt the MenuItem if more users/channels log in) I want to unsubscribe from events while the menu is open and then resubscribe afterwards. However, while FreezeUpdates() run perfectly in the above sample, ResumeUpdates for some reason give a "No target found for method ResumeUpdates." exception.

I imagine the problem is that somehow the visual tree changes on the closing event of the ContextMenu meaning the method is no longer found, but is there any possible workaround for this?

War es hilfreich?

Lösung

How typical is this? After spending two days coming back to this problem, I post here and then 5 minutes later figure it out...

The key is to instead put:

<Grid HorizontalAlignment="Center" Tag="{Binding DataContext, ElementName=IndividualQueue}" cal:Message.Attach="[Event ContextMenuClosing] = [Action ResumeUpdates()]">

And catch the closing event from the Grid instead of the ContextMenu itself.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top