【问题标题】:WPF MVVM Light - About views communicatingWPF MVVM Light - 关于视图通信
【发布时间】:2014-07-14 19:56:33
【问题描述】:

我目前正在从事一个工作项目。我正在寻求外部设计意见,以及关于我所面临问题的一​​些一般信息。

我们有一个位于项目根目录中的 MainWindow.xaml 文件。在这个主窗口中是一些折叠堆栈面板、功能区工具栏等的设计和逻辑。

到目前为止,我们的想法是在每个堆栈面板中包含一个不同的面板,以帮助使代码整洁。视图位于“视图”文件夹中。所以为了清楚起见,MainWindow.xaml 和其他视图不在同一个目录中。如有必要,这是可以更改的。

所以这是我的问题/问题:我们有一个窗口('A'),一个带有可折叠堆栈面板的主面板,其中包含窗口'A'中的一些信息('B')。然后有另一个堆栈面板来管理'B'中的内容,(折叠/可见)('C')。

“A”包含一个用于显示/折叠“B”的切换按钮。 “B”包含一个显示/折叠“C”的按钮。 “C”包含一个显示/折叠自身的按钮,“C”。

'C' 应该将其逻辑全部包含在一个视图中,因此 MainWindow ('A') 应该有一个简单的标记:

<StackPanel Style="{StaticResource FrameGradient}"  Tag="{Binding ElementName=ToggleButton}">
    <view:Content></view:Content>
</StackPanel>

目前,用于在“A”内切换按钮的绑定在样式中。在这种情况下,FrameGradient 具有如下触发器:

<Style x:Key="FrameGradient" TargetType="{x:Type StackPanel}">
    //Setter properties
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="False">
            <Setter Property="StackPanel.Visibility" Value="Collapsed" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="True">
            <Setter Property="StackPanel.Visibility" Value="Visible" />
        </DataTrigger>
    </Style.Triggers>
</Style>    

是否可以在“内容”视图中切换不在视图中的面板“C”?我觉得我在这里错过了 XAML 的核心思想。我发现了一个“便宜”的解决方法,就是将内容视图中的“关闭”按钮放在标签之外,但这会导致样式问题,我觉得我不应该做这样愚蠢的事情。同样,这个想法是堆栈面板“C”的切换按钮包含在另一个视图中,我希望能够从另一个视图切换它。

如果我不够清楚,我深表歉意,如果需要,我会在此处向任何询问的人提供更多信息。

更新 我有一些时间来实际添加我正在使用的代码,这样可能更有意义。

MainWindow.xaml - 筛选器面板的逻辑(位于根目录中)

    <StackPanel Grid.Row="1" Grid.Column="4" Visibility="Collapsed" Style="{StaticResource FrameGradient}">
        <Grid x:Name="FilterContentGrid">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <view:Filters></view:Filters>
        </Grid>
    </StackPanel>

Filters.xaml - 过滤器视图的逻辑(位于 /Views) 文件中需要折叠上述 StackPanel 的按钮。

<Button x:Name="FilterManagementCloseButton" Content="CLOSE"></Button>

Theme.Xaml - 所有样式的逻辑(与 MainWindow.xaml 和 App.xaml 一起位于根目录中) 按钮样式

<Style x:Key="FilterManagementCloseButton" TargetType="Button">
    <Setter Property="Padding" Value="10,5,20,3" />

    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource AncestorType={x:Type Local:MainWindow}}}" Value="True">
            <Setter Property="StackPanel.Visibility" Value="Visible" />
        </DataTrigger>
    </Style.Triggers>
</Style>

最后,FrameGradient 样式也位于 Theme.xaml 中

<Style x:Key="FrameGradient" TargetType="{x:Type StackPanel}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="False">
            <Setter Property="StackPanel.Visibility" Value="Collapsed" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="True">
            <Setter Property="StackPanel.Visibility" Value="Visible" />
        </DataTrigger>
    </Style.Triggers>
</Style>

所以,我希望这能让事情更清楚。我希望 Filters.xaml 中的 CLOSE 按钮折叠位于 MainWindow 中的堆栈面板。我意识到这段代码现在一团糟。

【问题讨论】:

  • 我假设创建一个实现 INPC 的 MainWindowViewModel 类会有所帮助。将MainWindowbool 属性如IsBVisible and IsCVisible, maybe with logic in the getters sounds applicable. Then, set the DataContext` 添加到MainWindowViewModel 的实例中,您可以通过执行&lt;StackPanel IsVisible="{Binding IsBVisible, Converter={StaticResource VisConverter}}" /&gt; 之类的操作来绑定,假设您有'
    在我的特殊情况下,我有一个 MainWindowVM,但我也有一个 FiltersVM(我要显示的面板)。主窗口中的每个面板都有自己的虚拟机。

标签: wpf xaml mvvm mvvm-light


【解决方案1】:

是否可以在“内容”视图中切换面板“C”, 哪个不在视图内?

创建一个共享虚拟机,其他虚拟机将拥有一个可以访问的属性。此 VM 可以在其他 VM 的初始化期间加载。为了允许更改发生,将 INotifyProperty(ies) 放在共享 VM 上,然后它将在所有视图中标记所需的逻辑。最后,将目标控件正常绑定到您的数据上下文,但共享 VM 目标属性的子路径除外。

因此,当一个视图切换(双向绑定)共享属性时,它会反映在目标面板的视图上。

更新示例

这里的想法是为 AppPage 创建一个视图模型。该虚拟机将保存在所有视图模型之间共享的通用标志。随后创建的每个 ViewModel 都会引用 AppPage 的 viewmodel。

下面的示例是一个主页,其中AppVM 包含一个标志,该标志通知主页是否正在进行登录。如果是并且该值为true,则将启用主页上的绑定按钮。

随后,主页可以覆盖 appvm 并通过一个可以直接更改按钮是否启用的有界复选框在该标志内放置一个新值;从而更改进程中所有其他 VM 的标志。

这里是 Mainpage VM,在本例中,我只是创建了 AppVM,但它可以传入或从其他地方的静态引用中获取。还要注意当AVs (appVM) 属性发生变化时我是如何不在乎的;本示例不需要它(我们没有将任何东西绑定到 AppVM,只是需要监控它的属性)。

public class MainVM : INotifyPropertyChanged
{
    public AppVM AV { get; set; }

     public MainVM()
     {
        AV = new AppVM() { LoginInProcess = true };
     }
}

这是 AppVm

   public class AppVM : INotifyPropertyChanged 
    {
        private bool _LoginInProcess;

        public bool LoginInProcess
        {
            get {  return _LoginInProcess; }
            set { _LoginInProcess = value; OnPropertyChanged(); }
        }
}

这是 MainPage 的 Xaml,其中 datacontext 已设置为 MainVM 的实例:

<StackPanel Orientation="Vertical">
    <CheckBox Content="Override"
              IsChecked="{Binding AV.LoginInProcess, Mode=TwoWay}"/>

    <Button Content="Login"
            IsEnabled="{Binding AV.LoginInProcess}"
            Width="75" />

</StackPanel>

我的 MVVM 来自我的博客文章 Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding,它解释了此示例中其他缺失的项目,例如主页的数据上下文加载。

【讨论】:

  • 谢谢,我试试这个。
  • 你有一个代码示例吗?您似乎想创建一种将视图模型与其他视图模型相关联的方法……这是否违反了 MvvM 框架的理念?
  • @user1296981 只要一个人的数据存在于不是model 或不是view 的东西上,这就是MVVM 的概念。 ViewModel 的属性只是对另一个 VM 的引用,基本上是数据的层次结构;不多不少。
  • 我遇到了一些问题,因为我需要隐藏/显示的控件不在主窗口中,但处理它的切换按钮位于 MainWindow.xaml 中。我可以'似乎没有将可见性绑定到我从另一个视图中创建的 AV.IsVisible 属性,因为它们没有使用相同的 VM。
  • @user1296981 你提到的其他虚拟机有 AV 属性吗?
【解决方案2】:

您可以使用RelativeSource Bindings 将子视图绑定到父视图模型中的属性。假设您在MainWindow.xaml 中有一个ToggleButton,它是绑定到名为IsChecked 的属性的数据,该属性在数据绑定到MainWindow DataContext 属性的对象中声明。您可以使用 RelativeSource Binding 从任何子视图将数据绑定到同一属性,如下所示:

<Style x:Key="FrameGradient" TargetType="{x:Type StackPanel}">
    <Setter Property="StackPanel.Visibility" Value="Visible" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=DataContext.IsChecked, RelativeSource={
            RelativeSource AncestorType={x:Type Local:MainWindow}}}" Value="False">
            <Setter Property="StackPanel.Visibility" Value="Collapsed" />
        </DataTrigger>
        <!-- Note that there is no need for two Triggers here -->
        <!-- One Setter and one Trigger is enough -->
    </Style.Triggers>
</Style> 

【讨论】:

  • 您是在假设
  • A RelativeSource BindingRelativeSource Binding,无论它在哪里声明。
  • 我在尝试使其正常工作时仍然遇到问题。 Path=DataContext.IsChecked 的绑定对我来说没有多大意义。我的印象是 IsChecked 仅适用于切换按钮、复选框等。我想绑定操作以将面板折叠到常规按钮,除非有更好的方法。此外,由于某种原因,带有堆栈面板折叠按钮的过滤器的 UserControl 无法看到我的 Theme.xaml 文件,即使它在 App.xaml 中配置为应用程序资源。
  • 再次阅读我的答案。 MainWindow.xaml 中的ToggleButton 是绑定到名为IsChecked 的属性的数据,在数据绑定到MainWindow DataContext 属性的对象中声明 .
猜你喜欢
  • 1970-01-01
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多