UserControl 和 Window 的一个 ViewModel 或单独的 ViewModel

2024-02-09

I have MainWindow and AddEdit UserControl。里面MainWindow我渲染这个 AddEdit 就像<Views:AddEditData />,之前将此命名空间添加到 Window 元素中:

xmlns:Views="clr-namespace:MyProject.WPF.Views"

+++++++++++++++ ++++++++++++++++
ListOfData    + + DataDetails  +
              + +              +
   DataOne    + + Name: txtBox1+
   DataTwo    + +              +
   DataThree  + +              +
              + +  Save data   +
+++++++++++++++ ++++++++++++++++

当用户选择左侧的数据(例如 DataTwo)时,我想在 AddEdit 用户控件(DataDetails 面板)内显示它的属性(为了简单起见,仅显示 Name 属性)。

从此UserControl与 MainWindow 分开存储,我应该使用相同的 MainWindowViewModel 和相同的数据上下文,还是应该为 AddEdit 创建单独的 ViewModelUserControl?

希望这听起来很清楚,如果不清楚,请询问详细信息。


Part 1. Display the properties of the control in MVVM

正如我在评论中所说:

在 MVVM ViewModel 中不应该知道其所在的控件。在这种情况下,请使用附加行为或在视图中保留相同的侧逻辑

ViewModel不直接与View,所以仅仅引用控件的名称是不正确的。最好在里面设置一个属性Model,并将其绑定到View via ViewModel,但财产Name不支持绑定(引用自MSDN http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.name.aspx):

数据绑定名称在技术上是可行的,但这是一种极其罕见的情况,因为数据绑定名称无法满足属性的主要预期目的:为代码隐藏提供标识符连接点。

所以我建议使用Tag财产或Uid。在我的示例中(下面给出),我使用Uid用于这些目的的财产。

Part 2. Communication via ViewModels (pattern Mediator)

其具体实施方式有多种Mediator模式,但我最喜欢的实现是XAML Guy,简单明了——调解者模式 http://code.msdn.microsoft.com/windowsdesktop/The-Mediator-Pattern-c2c07b54.

Implementation code

public static class Mediator
{
    static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();

    static public void Register(string token, Action<object> callback)
    {
        if (!pl_dict.ContainsKey(token))
        {
            var list = new List<Action<object>>();
            list.Add(callback);
            pl_dict.Add(token, list);
        }
        else
        {
            bool found = false;
            foreach (var item in pl_dict[token])
                if (item.Method.ToString() == callback.Method.ToString())
                    found = true;
            if (!found)
                pl_dict[token].Add(callback);
        }
    }

    static public void Unregister(string token, Action<object> callback)
    {
        if (pl_dict.ContainsKey(token))
        {
            pl_dict[token].Remove(callback);
        }
    }

    static public void NotifyColleagues(string token, object args)
    {
        if (pl_dict.ContainsKey(token))
        {
            foreach (var callback in pl_dict[token])
                callback(args);
        }
    }
}

为了展示他的工作,我创建了一个小例子,其中包含两个Views,各有各的ViewModel and Model.

项目结构如下图所示:

Output

当您单击按钮时,ListOfDataViewModel通过调解器与 DataDetails 进行通信ViewModel, thus:

Mediator.NotifyColleagues("ShowDetails", true);
Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);

所有与属性交互的过程都必须注册它们的属性ViewModel像这样:

private void ShowDetails_Mediator(object args)
{
    bool showDetails = (bool)args;

    if (showDetails == true)
    {
        DataDetailsModel.IsVisible = true;
    }
    else
    {
        DataDetailsModel.IsVisible = false;
    }
}

private void SetSelectedFruit_Mediator(object args)
{
    string selectedFruit = (string)args;

    DataDetailsModel.SelectedFruit = selectedFruit;
}

public DataDetailsViewModel() 
{
    DataDetailsModel = new DataDetailsModel();

    Mediator.Register("ShowDetails", ShowDetails_Mediator);
    Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator);
}

在示例中我使用了DataTemplate反而UserControl。以下是该项目的主要部分:

MainWindow.xaml

<Window x:Class="CommunicateWithVM.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels"
    Title="MainWindow" 
    WindowStartupLocation="CenterScreen"
    Height="350"
    Width="525">

    <Grid>
        <ContentControl Name="ListOfData"
                        ContentTemplate="{StaticResource ListOfDataView}">

            <ViewModels:ListOfDataViewModel />            
        </ContentControl>

        <ContentControl Name="DataDetails"
                        ContentTemplate="{StaticResource DataDetailsView}">

            <ViewModels:DataDetailsViewModel />
        </ContentControl>
    </Grid>
</Window> 

Models

DataDetailsModel

public class DataDetailsModel : NotificationObject
{
    #region SelectedFruit

    private string _selectedFruit = "";

    public string SelectedFruit
    {
        get
        {
            return _selectedFruit;
        }

        set
        {
            _selectedFruit = value;
            NotifyPropertyChanged("SelectedFruit");
        }
    }

    #endregion

    #region IsVisible

    private bool _isVisible = false;

    public bool IsVisible
    {
        get
        {
            return _isVisible;
        }

        set
        {
            _isVisible = value;
            NotifyPropertyChanged("IsVisible");
        }
    }

    #endregion
}

ListOfDataModel

public class ListOfDataModel : NotificationObject
{
    #region FruitGreen

    private string _fruitGreen = "Apple";

    public string FruitGreen
    {
        get
        {
            return _fruitGreen;
        }

        set
        {
            _fruitGreen = value;
            NotifyPropertyChanged("FruitGreen");
        }
    }

    #endregion

    #region FruitYellow

    private string _fruitYellow = "Limon";

    public string FruitYellow
    {
        get
        {
            return _fruitYellow;
        }

        set
        {
            _fruitYellow = value;
            NotifyPropertyChanged("FruitYellow");
        }
    }

    #endregion
}

ViewModels

DataDetailsViewModel

public class DataDetailsViewModel
{
    #region DataDetailsModel

    private DataDetailsModel _dataDetailsModel = null;

    public DataDetailsModel DataDetailsModel
    {
        get
        {
            return _dataDetailsModel;
        }

        set
        {
            _dataDetailsModel = value;
        }
    }

    #endregion

    #region ShowDetails_Mediator

    private void ShowDetails_Mediator(object args)
    {
        bool showDetails = (bool)args;

        if (showDetails == true)
        {
            DataDetailsModel.IsVisible = true;
        }
        else
        {
            DataDetailsModel.IsVisible = false;
        }
    }

    #endregion

    #region SetSelectedFruit_Mediator

    private void SetSelectedFruit_Mediator(object args)
    {
        string selectedFruit = (string)args;

        DataDetailsModel.SelectedFruit = selectedFruit;
    }

    #endregion

    #region DataDetailsViewModel Constructor

    public DataDetailsViewModel() 
    {
        DataDetailsModel = new DataDetailsModel();

        Mediator.Register("ShowDetails", ShowDetails_Mediator);
        Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator);
    }

    #endregion
}

ListOfDataViewModel

public class ListOfDataViewModel
{
    #region ListOfDataModel

    private ListOfDataModel _listOfDataModel = null;

    public ListOfDataModel ListOfDataModel
    {
        get
        {
            return _listOfDataModel;
        }

        set
        {
            _listOfDataModel = value;
        }
    }

    #endregion

    #region GreenButtonCommand

    private ICommand _greenButtonCommand = null;

    public ICommand GreenButtonCommand
    {
        get
        {
            if (_greenButtonCommand == null)
            {
                _greenButtonCommand = new RelayCommand(param => this.GreenButton(), null);
            }

            return _greenButtonCommand;
        }
    }

    private void GreenButton()
    {
        Mediator.NotifyColleagues("ShowDetails", true);
        Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);
    }

    #endregion

    #region YellowButtonCommand

    private ICommand _yellowButtonCommand = null;

    public ICommand YellowButtonCommand
    {
        get
        {
            if (_yellowButtonCommand == null)
            {
                _yellowButtonCommand = new RelayCommand(param => this.YellowButton(), null);
            }

            return _yellowButtonCommand;
        }
    }

    private void YellowButton()
    {
        Mediator.NotifyColleagues("ShowDetails", true);
        Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitYellow);
    }

    #endregion

    #region ListOfDataViewModel Constructor

    public ListOfDataViewModel() 
    {
        ListOfDataModel = new ListOfDataModel();
    }

    #endregion
}

Views

DataDetailsView

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">

    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />

    <DataTemplate x:Key="DataDetailsView" DataType="{x:Type ViewModels:DataDetailsViewModel}">
        <StackPanel Width="200" 
                    Background="AliceBlue"
                    HorizontalAlignment="Right"
                    Visibility="{Binding Path=DataDetailsModel.IsVisible, 
                                         Converter={StaticResource BooleanToVisibilityConverter}}">

            <TextBlock Text="Fruit: " />
            <TextBlock Text="{Binding Path=DataDetailsModel.SelectedFruit}" />
        </StackPanel>
    </DataTemplate>    
</ResourceDictionary>

ListOfDataView

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">

    <DataTemplate x:Key="ListOfDataView" DataType="{x:Type ViewModels:ListOfDataViewModel}">
        <StackPanel Width="200" 
                    Background="Azure"
                    HorizontalAlignment="Left">

            <Button Uid="{Binding Path=ListOfDataModel.FruitGreen}"
                    Content="GreenButton"
                    Command="{Binding Path=GreenButtonCommand}" />

            <Button Uid="{Binding Path=ListOfDataModel.FruitYellow}"
                    Content="YellowButton" 
                    Command="{Binding Path=YellowButtonCommand}" />
        </StackPanel>
    </DataTemplate>    
</ResourceDictionary>

该项目可在此处获得.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UserControl 和 Window 的一个 ViewModel 或单独的 ViewModel 的相关文章

  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • C++ 子字符串返回错误结果

    我有这个字符串 std string date 20121020 我正在做 std cout lt lt Date lt lt date lt lt n std cout lt lt Year lt lt date substr 0 4 l
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • 在 Visual Studio 2008 上设置预调试事件

    我想在 Visual Studio 中开始调试程序之前运行一个任务 我每次调试程序时都需要运行此任务 因此构建后事件还不够好 我查看了设置的 调试 选项卡 但没有这样的选项 有什么办法可以做到这一点吗 你唯一可以尝试的 IMO 就是尝试Co
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • 如果使用 SingleOrDefault() 并在数字列表中搜索不在列表中的数字,如何返回 null?

    使用查询正数列表时SingleOrDefault 当在列表中找不到数字时 如何返回 null 或像 1 这样的自定义值 而不是类型的默认值 在本例中为 0 你可以使用 var first theIntegers Cast
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 线程睡眠和Windows服务

    我正在开发一个 Windows 服务 该服务存在一些问题Thread Sleep 所以我想我会尝试使用计时器 因为这个问题建议 在 Windows 服务中使用 Thread Sleep https stackoverflow com que
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • 用累积值表示设置时间

    调度问题有很多系列 我正在研究一个问题 我有一系列的工作 任务 需要从一个家庭过渡到另一个家庭 需要重新配置机器 设置时间 我在用着cumulatives 2 3 解决这个问题 但我不确定设置时间如何 可以表达 在这个小例子中 我有 10
  • 删除主键(聚集索引)以提高插入性能

    我们一直在经历 SQL 超时 并已确定该瓶颈是审核表 我们系统中的所有表都包含插入 更新和删除触发器 这些触发器会导致新的审核记录 这意味着审计表是系统中最大且最繁忙的表 然而数据只进入 永远不会出来 在这个系统下 所以没有select需要
  • Eclipse 索引 - 各种选项的作用是什么

    当你right click gt index在一个项目上有几个选项 Rebuild 刷新所有文件 使用修改的文件进行更新 重新解决未解决的包含 我每次都只是点击重建 但现在我正在做一个巨大的项目 但负担不起这样做 当我修改文件时 无论是 c
  • Android 波纹按钮与背景

    我使用 XML 在 android 5 0 中成功创建了一个波纹按钮 我的代码如下
  • 使用 LINQ 将多个列表合并为一个列表

    是否有一种灵活的方法可以使用 LINQ 将多个列表合并为单个列表 以有效地复制此内容 public class RGB public int Red get set public int Green get set public int B
  • 使用 Alpha 将 8 位 PNG 转换为 32 位

    我有一张图片 PNG image data 403 x 343 8 bit colormap non interlaced 我想将其转换为 Google Play 图标大小 32 bit with alpha 我该怎么做 使用修复它http
  • Server.Transfer 和 Response.Redirect 哪一个更好

    哪一个更好 Server Transfer or Response Redirect 我正在寻找一些解释 它们有不同的功能 更好的定义取决于您想要做什么 Response Redirect 告诉客户端访问一个新地址 该地址可以是任何地方 S
  • 在 Java Servlet 上下文中,URL 重写和转发之间有什么区别?

    作为 Java Web 应用程序的开发人员 我什么时候需要使用 URL 重写 URL 重写和转发之间有什么区别 我在其他网站上进行了搜索 我得到了矛盾的信息 具体取决于您与谁交谈 例如 SEO 人员会以不同的方式回答这个问题 AFAIK 在
  • 通过actionscript 3在Flash中设置舞台消失点

    我正在与几位设计师合作 他们每个人都创建了在同一个 Flash 应用程序中使用的 3D 动画 使用 fp 10 功能 每个 3D 动画都是一个独特的影片剪辑 最终将成为同一 fla 文件的一部分 我遇到的问题是每个影片剪辑都是在单独的 fl
  • react-native fetch返回状态码+json

    我在react native中使用fetch来进行API调用 我需要获取状态代码 200 401 404 和响应数据 这项工作是为了获取响应数据 return fetch url then response gt return respon
  • 为什么命令完成后我的 stderr 重定向没有结束?我该如何解决它?

    在 Windows 中 无论是在命令行还是批处理文件中 命令DIR 2 gt NUL 3 gt 2 你可以替换DIR任何东西 即使不是文件或命令 都会使所有错误从那时起丢失 除非你写2 gt CON 每次命令之后 为什么 CMD 还要这样做
  • 如何告诉 R 解释器如何使用代理服务器?

    我试图让 R 在 Windows 上运行 从 Internet 下载一些软件包 但下载失败 因为我无法让它正确使用必要的代理服务器 当我尝试 Windows 菜单选项时的输出文本Packages gt 安装软件包 选择一个CRAN镜像是 g
  • 如何将 Ninject 集成到 ASP.NET Core 2.0 Web 应用程序中?

    我发现 Ninject 最近引入了对 NET Standard 2 0 NET Core 2 0 的支持 https www nuget org packages Ninject 3 3 0 但是 我找不到任何扩展来将其实际集成到 Web
  • Docker:容器在专用网络上找不到域

    我试图解决在不同主机上运行相同的 docker 容器的问题 其中一个容器可以在专用网络上找到 ping nslookup 域 而另一个容器则不能 一台主机是 OSX 10 11 另一台是 Ubuntu 16 04 两者都运行 docker
  • .htaccess 单页301重定向

    网站重新设计后 我有几个页面需要重定向 所有内容都保留在同一个域中 只有一些内容被重新组织和 或重命名 它们的形式如下 contact php is now 联系我们 php 使用 htaccess 文件 我添加了这一行 这是我最推荐的一行
  • 使用 HTML5 和 AngularJS 拖动表格列

    http jsfiddle net asutosh 82qum http jsfiddle net asutosh 82qum div div table border 4 thead th div hd div th thead tr t
  • JQuery 附加 javascript

    我尝试创建模块化应用程序 因此每个页面都包含自己的 html 和 javascript 代码 我应该动态加载所有代码 如下所示 var s document createElement script s type text javascri
  • 解包参数:只有命名参数可以跟在*表达式后面

    以下代码在 Python 中运行得非常好 def f x y z return x y z a 1 2 f 3 a 的要素a像你这样称呼它一样打开包装f 3 1 2 它返回 3 1 2 精彩的 但我无法解开其中的元素a进入first两个论点
  • 类型安全:来自对象的未经检查的强制转换

    我尝试将一个对象转换为我的 Action 类 但它会导致警告 Type safety Unchecked cast from Object to Action
  • UserControl 和 Window 的一个 ViewModel 或单独的 ViewModel

    I have MainWindow and AddEdit UserControl 里面MainWindow我渲染这个 AddEdit 就像