Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么使用Caliburn Micro Conductor.OneActive时,混合交互事件会触发多次触发?_C#_Wpf_Mvvm_Caliburn.micro_Blend - Fatal编程技术网

C# 为什么使用Caliburn Micro Conductor.OneActive时,混合交互事件会触发多次触发?

C# 为什么使用Caliburn Micro Conductor.OneActive时,混合交互事件会触发多次触发?,c#,wpf,mvvm,caliburn.micro,blend,C#,Wpf,Mvvm,Caliburn.micro,Blend,[请注意,我在第一次尝试诊断时提出了错误的问题-现在已更正。] 我有一个WPF应用程序,它有一个从Conductor.Collection.OneActive继承的主窗口。它处理导航请求并保留viewmodels的缓存,以便保持状态。此私有集合与基本集合几乎相同。Items集合但并非所有viewmodels都是IScreen 当我们从一个活动项移动到另一个活动项时,一切正常,状态保持不变。然而,交互触发器有一个bug。当活动项为IScreen时,触发器会在每次导航时额外触发一次,就好像每次都重新

[请注意,我在第一次尝试诊断时提出了错误的问题-现在已更正。]

我有一个WPF应用程序,它有一个从Conductor.Collection.OneActive继承的主窗口。它处理导航请求并保留viewmodels的缓存,以便保持状态。此私有集合与基本集合几乎相同。Items集合但并非所有viewmodels都是IScreen

当我们从一个活动项移动到另一个活动项时,一切正常,状态保持不变。然而,交互触发器有一个bug。当活动项为IScreen时,触发器会在每次导航时额外触发一次,就好像每次都重新连接一样;普通触发器不这样做,只有来自交互库的触发器才这样做。如果活动项不是IScreen-只是从PropertyChangedBase继承-我们看不到这个问题,但是在导航期间我们也会丢失视图的状态

如果导航到视图四次,事件将触发四次、五次、五次,依此类推

这看起来是一样的,但我不能使用他的解决方案,因为我不知道具体的viewmodels是什么,也不能为它们创建公共属性

我的主窗口类如下所示:

public sealed class MainWindowViewModel : Conductor<object>.Collection.OneActive, IHandle<NavigateToUriMessage>
{
    public void Handle(NavigateToUriMessage message)
    {
        var ignoredUris = RibbonUri.GetItems().Where(t => t.SubTabs.Count > 0).Select(t => t.Uri);

        Func<string, bool> isMatch = uri => uri == message.Uri;

        if (isMatch(RibbonUri.BookkeepingBatchView.Uri))
            GetAndActivateViewModel<BatchPanelViewModel>(message);           
        ...
    }

    private T GetAndActivateViewModel<T>(NavigateToUriMessage message) where T : class
    {
        var vm = GetViewModel<T>(message.Uri);
        ActivateItem(vm);
        return (T) vm;
    }

    private object GetViewModel<T>(string uri) where T : class
    {
        if (viewModelCache.ContainsKey(uri))
            return viewModelCache[uri];
        var vm = viewModelFactory.Create<T>();
        viewModelCache.Add(uri, vm);
        return vm;
    }
}
<Window x:Class="Ui.Views.MainWindowView"
      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:d="http://schemas.microsoft.com/expression/blend/2008" 
      d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel, IsDesignTimeCreatable=False}"
      mc:Ignorable="d"
      WindowState="Maximized" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="1"/>
            <RowDefinition/>
            <RowDefinition Height="1"/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>
        <ContentControl x:Name="MainRibbon" Grid.Row="0" Margin="0" />
        <Grid Background="#FFBBBBBB" Grid.Row="1" />
        <ContentControl Grid.Row="2" x:Name="ActiveItem" Background="White" />
        <Grid  Grid.Row="3" Background="#FFBBBBBB"/>
        <ContentControl x:Name="BottomStatusBar" Grid.Row="4"/>
    </Grid>
</Window>
<DataGrid Focusable="True" FocusVisualStyle="{x:Null}" SelectionMode="Single" HeadersVisibility="None" IsReadOnly="True" GridLinesVisibility="None" AutoGenerateColumns="False" Background="Transparent" BorderThickness="0"  Grid.Row="3" Grid.RowSpan="2">
            <i:Interaction.Triggers>
                <ei:KeyTrigger Key="Left" FiredOn="KeyUp" ActiveOnFocus="True" >
                    <cal:ActionMessage MethodName="TryCollapseSelectedItem"/>
                </ei:KeyTrigger>
                <ei:KeyTrigger Key="Right" FiredOn="KeyUp" ActiveOnFocus="True" >
                    <cal:ActionMessage MethodName="TryExpandSelectedItem"/>
                </ei:KeyTrigger>
            </i:Interaction.Triggers>
 ....
公共密封类主窗口视图模型:Conductor.Collection.OneActive,IHandle
{
公共无效句柄(NavigateToUriMessage消息)
{
var ignoredUris=RibbonUri.GetItems()。其中(t=>t.SubTabs.Count>0)。选择(t=>t.Uri);
Func isMatch=uri=>uri==message.uri;
if(isMatch(RibbonUri.BookkeepingBatchView.Uri))
GetAndActivateViewModel(消息);
...
}
private T GetAndActivateViewModel(NavigateToUriMessage消息),其中T:class
{
var vm=GetViewModel(message.Uri);
激活肽(vm);
返回(T)vm;
}
私有对象GetViewModel(字符串uri),其中T:class
{
if(viewModelCache.ContainsKey(uri))
返回viewModelCache[uri];
var vm=viewModelFactory.Create();
添加(uri,vm);
返回虚拟机;
}
}
我的主窗口XAML如下所示:

public sealed class MainWindowViewModel : Conductor<object>.Collection.OneActive, IHandle<NavigateToUriMessage>
{
    public void Handle(NavigateToUriMessage message)
    {
        var ignoredUris = RibbonUri.GetItems().Where(t => t.SubTabs.Count > 0).Select(t => t.Uri);

        Func<string, bool> isMatch = uri => uri == message.Uri;

        if (isMatch(RibbonUri.BookkeepingBatchView.Uri))
            GetAndActivateViewModel<BatchPanelViewModel>(message);           
        ...
    }

    private T GetAndActivateViewModel<T>(NavigateToUriMessage message) where T : class
    {
        var vm = GetViewModel<T>(message.Uri);
        ActivateItem(vm);
        return (T) vm;
    }

    private object GetViewModel<T>(string uri) where T : class
    {
        if (viewModelCache.ContainsKey(uri))
            return viewModelCache[uri];
        var vm = viewModelFactory.Create<T>();
        viewModelCache.Add(uri, vm);
        return vm;
    }
}
<Window x:Class="Ui.Views.MainWindowView"
      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:d="http://schemas.microsoft.com/expression/blend/2008" 
      d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel, IsDesignTimeCreatable=False}"
      mc:Ignorable="d"
      WindowState="Maximized" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="1"/>
            <RowDefinition/>
            <RowDefinition Height="1"/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>
        <ContentControl x:Name="MainRibbon" Grid.Row="0" Margin="0" />
        <Grid Background="#FFBBBBBB" Grid.Row="1" />
        <ContentControl Grid.Row="2" x:Name="ActiveItem" Background="White" />
        <Grid  Grid.Row="3" Background="#FFBBBBBB"/>
        <ContentControl x:Name="BottomStatusBar" Grid.Row="4"/>
    </Grid>
</Window>
<DataGrid Focusable="True" FocusVisualStyle="{x:Null}" SelectionMode="Single" HeadersVisibility="None" IsReadOnly="True" GridLinesVisibility="None" AutoGenerateColumns="False" Background="Transparent" BorderThickness="0"  Grid.Row="3" Grid.RowSpan="2">
            <i:Interaction.Triggers>
                <ei:KeyTrigger Key="Left" FiredOn="KeyUp" ActiveOnFocus="True" >
                    <cal:ActionMessage MethodName="TryCollapseSelectedItem"/>
                </ei:KeyTrigger>
                <ei:KeyTrigger Key="Right" FiredOn="KeyUp" ActiveOnFocus="True" >
                    <cal:ActionMessage MethodName="TryExpandSelectedItem"/>
                </ei:KeyTrigger>
            </i:Interaction.Triggers>
 ....

活动项视图中的XAML如下所示:

public sealed class MainWindowViewModel : Conductor<object>.Collection.OneActive, IHandle<NavigateToUriMessage>
{
    public void Handle(NavigateToUriMessage message)
    {
        var ignoredUris = RibbonUri.GetItems().Where(t => t.SubTabs.Count > 0).Select(t => t.Uri);

        Func<string, bool> isMatch = uri => uri == message.Uri;

        if (isMatch(RibbonUri.BookkeepingBatchView.Uri))
            GetAndActivateViewModel<BatchPanelViewModel>(message);           
        ...
    }

    private T GetAndActivateViewModel<T>(NavigateToUriMessage message) where T : class
    {
        var vm = GetViewModel<T>(message.Uri);
        ActivateItem(vm);
        return (T) vm;
    }

    private object GetViewModel<T>(string uri) where T : class
    {
        if (viewModelCache.ContainsKey(uri))
            return viewModelCache[uri];
        var vm = viewModelFactory.Create<T>();
        viewModelCache.Add(uri, vm);
        return vm;
    }
}
<Window x:Class="Ui.Views.MainWindowView"
      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:d="http://schemas.microsoft.com/expression/blend/2008" 
      d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel, IsDesignTimeCreatable=False}"
      mc:Ignorable="d"
      WindowState="Maximized" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="1"/>
            <RowDefinition/>
            <RowDefinition Height="1"/>
            <RowDefinition Height="25"/>
        </Grid.RowDefinitions>
        <ContentControl x:Name="MainRibbon" Grid.Row="0" Margin="0" />
        <Grid Background="#FFBBBBBB" Grid.Row="1" />
        <ContentControl Grid.Row="2" x:Name="ActiveItem" Background="White" />
        <Grid  Grid.Row="3" Background="#FFBBBBBB"/>
        <ContentControl x:Name="BottomStatusBar" Grid.Row="4"/>
    </Grid>
</Window>
<DataGrid Focusable="True" FocusVisualStyle="{x:Null}" SelectionMode="Single" HeadersVisibility="None" IsReadOnly="True" GridLinesVisibility="None" AutoGenerateColumns="False" Background="Transparent" BorderThickness="0"  Grid.Row="3" Grid.RowSpan="2">
            <i:Interaction.Triggers>
                <ei:KeyTrigger Key="Left" FiredOn="KeyUp" ActiveOnFocus="True" >
                    <cal:ActionMessage MethodName="TryCollapseSelectedItem"/>
                </ei:KeyTrigger>
                <ei:KeyTrigger Key="Right" FiredOn="KeyUp" ActiveOnFocus="True" >
                    <cal:ActionMessage MethodName="TryExpandSelectedItem"/>
                </ei:KeyTrigger>
            </i:Interaction.Triggers>
 ....

....
似乎,尽管隐藏在对公认答案的评论中。KeyTrigger类有问题,每次将子控件重新加载到ContentControl时都会附加事件@gunter说,“真正的问题似乎是OnLoaded上的钩子和tab控件上的元素得到多个OnLoaded事件。”然后@dain说,“好的,我明白了。KeyTrigger是一个公共类,所以你可以扩展它并覆盖OneEvent以防止多个事件处理程序连接?”

因此,我实现了@dain的建议,重写了KeyTrigger类,并多次停止连接事件:

public class MyKeyTrigger : KeyTrigger
{
    private bool eventAttached;

    protected override void OnEvent(EventArgs eventArgs)
    {
        if (eventAttached) return;
        base.OnEvent(eventArgs);
        eventAttached = true;
    }

    protected override void OnDetaching()
    {
        eventAttached = false;
        base.OnDetaching();
    }

我已经能够更准确地诊断这个问题,因为这里有一个问题的答案:当设置ActiveItem时,子控件加载的事件将触发。KeyTrigger是有线加载的,所以要多加载。如果我创建自己的KeyTrigger类并从KeyTrigger继承,我可以重写OnEvent方法并停止多个附件。但是,我想知道每次激活它们时,有多少其他事情会受到加载事件触发的影响?是我做错了什么,还是这只是我可以绕过的KeyTrigger的一个问题。使用反编译器进行的进一步调查表明,KeyTrigger在加载期间附加了一个按键事件处理程序,但当UserControl卸载时,它不会得到一个OnDetaching调用,因此它是不对称的。因此,UserControl每次被激活并连接KeyTrigger时都会获得一个加载事件。