MVVM + WPF + Popup = 懵逼

26

我正在修改我的WPF应用程序,以符合MVVM的要求。我已经接近完成了,但还有一些问题需要解决。

我有一个GUI元素(目前是一个按钮,但也可以是标签),当鼠标进入时,它会触发MouseEnter事件,并在代码后台创建一个弹出窗口。该弹出窗口由一个简单的StackPanel布局和多个按钮组成。每个按钮的Click事件目前都分配给相同的事件处理程序,只是我更改Tag属性来进行简单(低劣)的命令参数。我正在考虑在MVVM中正确实现这个功能,因为我目前的做法非常丑陋。

为了解决这个问题,我认为我应该朝着以下方向前进,但会感激您提供的任何额外意见。 :)

  1. 在XAML中创建弹出窗口。由于我的弹出窗口内容是静态的,所以我应该能够完全在XAML中创建弹出窗口。此外,每个按钮都将绑定到相同的ICommand派生类。例如:

    <Popup x:Key="MyPopup" StaysOpen="False" Placement="Right">
        <Border Background="White" BorderBrush="Black" Padding="5" BorderThickness="2" CornerRadius="5">
            <StackPanel Orientation="Vertical">
                <Button Command="{Binding MyCommand}" CommandParameter="5">5</Button>
                <Button Command="{Binding MyCommand}" CommandParameter="10">10</Button>
                <Button Command="{Binding MyCommand}" CommandParameter="15">15</Button>
                <Button Command="{Binding MyCommand}" CommandParameter="20">20</Button>
            </StackPanel>
        </Border>
    </Popup>
    
  2. 通过触发器使弹出窗口弹出,例如:

                            <Button.Triggers>
                                <Trigger Property="Button.IsMouseOver" Value="True">
                                    <Setter TargetName="MyPopup" Property="IsOpen" Value="True" />
                                </Trigger>
                            </Button.Triggers>
    
  3. 我认为#1没问题,但#2让我挣扎了。这是正确的解决方法吗?如果是,获取弹出窗口的IsOpen属性应该使用什么XAML语法来设置为True?我找不到相关示例。

    如果我的想法完全错误,我希望听听其他选项。谢谢!


我看不出你的#2有什么问题,也许你可以提供更多关于你认为存在的问题的详细信息。 - Aviad P.
我得到了一个错误:“'IsOpen'成员无效,因为它没有限定类型名称。” - Dave
我也尝试了Popup.IsOpen并得到了相同的错误。 - Dave
1个回答

21

这是我的建议,我会先将可重用部分分离成资源,并在XAML中使用ContentControls进行引用。这适用于Popup以及Button。但是我不想仅限于一个按钮,所以我也会为此使用ContentControl

Popup模板:

<ControlTemplate x:Key="PopupTemplate">
    <Border 
                Background="White" 
                BorderBrush="Black" 
                Padding="5" 
                BorderThickness="2" 
                CornerRadius="5">
        <StackPanel Orientation="Vertical">
            <Button Command="{Binding MyCommand}" 
                            CommandParameter="5">5</Button>
            <Button Command="{Binding MyCommand}" 
                            CommandParameter="10">10</Button>
            <Button Command="{Binding MyCommand}" 
                            CommandParameter="15">15</Button>
            <Button Command="{Binding MyCommand}" 
                            CommandParameter="20">20</Button>
        </StackPanel>
    </Border>
</ControlTemplate>

ContentControl 模板:

<ControlTemplate x:Key="MyControlTemplate" TargetType="ContentControl">
    <Grid Name="MyControl">
        <ContentPresenter Content="{TemplateBinding Content}"/>
        <Popup Name="MyPopup" StaysOpen="True" Placement="Bottom">
            <ContentControl Template="{StaticResource PopupTemplate}"/>
        </Popup>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger SourceName="MyControl" 
                 Property="UIElement.IsMouseOver" 
                 Value="True">
            <Setter TargetName="MyPopup" 
                    Property="Popup.IsOpen" 
                    Value="True"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

现在,在主窗口的XAML中,我会创建以下内容:

<ContentControl Template="{StaticResource MyControlTemplate}">
    <Button Content="Test"/>
</ContentControl>

如果您有任何问题,我将很乐意回答。


1
谢谢帮助!这确实听起来是一个不错的方向。我会告诉你进展如何! - Dave
它几乎可以工作了!我现在唯一的问题是,当我在 GUI 中点击其他地方时,弹出窗口会消失(这正是我想要的),但当我再次悬停在 ContentControl 上时,弹出窗口不会再次出现。我是否需要在另一个触发器中显式设置 IsOpen 为 False?理想情况下,如果我点击不是 Popup 或 ContentControl 的某个地方(即某种复合/多触发器),我希望 Popup 消失。 - Dave
我认为问题可能是你在某个地方显式地将IsOpen设置为false,从而覆盖了触发器。你是在代码后台这样做的吗?也许是在弹出按钮的命令绑定处理程序中?如果是这样,那么你需要清除它的值以重新启用触发器... 这可能有点棘手。 - Aviad P.
我没有在代码后端的任何地方将IsOpen设置为False,但是我将StaysOpen属性设置为False,因为我希望它在我单击其他地方时消失。 我忘记了这一点。 那么,我应该将StaysOpen设置为True,然后添加MultiTrigger将IsOpen设置为False吗? - Dave
好的,我刚刚尝试了一下,结果并不是我所期望的。如果我将StaysOpen设置为True,然后添加MultiTrigger,它的行为就像预期的那样,除非我从ContentControl移动到Popup的速度太慢,否则Popup将会消失。有趣的是,如果我只是将StaysOpen设置为True而实现MultiTrigger,那么行为完全相同。 - Dave
我将此标记为答案,因为它非常有效。我非常兴奋,感谢您的帮助! - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接