Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/289.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# 使用MVVM更新WPF中的派生属性_C#_Wpf_Mvvm - Fatal编程技术网

C# 使用MVVM更新WPF中的派生属性

C# 使用MVVM更新WPF中的派生属性,c#,wpf,mvvm,C#,Wpf,Mvvm,当属性在UI中连接多个源时,可以使用什么模式来确保属性得到更新 例如,我有一个窗口标题的字符串属性。它显示应用程序名(常量字符串)、程序集版本(只读字符串)以及基于用户输入加载的类型的实例属性 有没有办法使title属性订阅instance属性,以便在加载实例时,title自动更新 现在,当加载配方时,它会更新title属性。但是我想把它颠倒过来,这样食谱就不知道名称了。它只是广播它已加载,然后需要对加载的配方做出反应的任何内容都将单独处理该事件 什么样的设计模式适合这种情况?如果正确,您需要开

当属性在UI中连接多个源时,可以使用什么模式来确保属性得到更新

例如,我有一个窗口标题的字符串属性。它显示应用程序名(常量字符串)、程序集版本(只读字符串)以及基于用户输入加载的类型的实例属性

有没有办法使title属性订阅instance属性,以便在加载实例时,title自动更新

现在,当加载配方时,它会更新title属性。但是我想把它颠倒过来,这样食谱就不知道名称了。它只是广播它已加载,然后需要对加载的配方做出反应的任何内容都将单独处理该事件


什么样的设计模式适合这种情况?

如果正确,您需要开始绑定工作。快速的谷歌搜索有很多结果,但这一个似乎涵盖了您需要的内容:


不确定这是否理想,但我的解决方案是处理MVVMLight提供的属性更改事件

    private Model.Recipe _recipe;
    public Model.Recipe Recipe
    {
        get { return _recipe; }
        set { Set(ref _recipe, value); }
    }

    public string MyProperty
    {
        get { return "Test " + Recipe.MyProperty; }
    }

    public MainViewModel()
    {
        PropertyChanged += MainViewModel_PropertyChanged;
        Recipe = new Model.Recipe();
    }

    private void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "Recipe": RaisePropertyChanged("MyProperty"); break;
        }
    }
不太喜欢MainViewModel_PropertyChanged将成为一个处理所有更改的大型switch语句。另一种方法是使用messenger

    private Model.Recipe _recipe;
    public Model.Recipe Recipe
    {
        get { return _recipe; }
        set { if (Set(ref _recipe, value)) { Messenger.Default.Send(value, "NewRecipe"); } }
    }

    public string MyProperty
    {
        get { return "Test " + Recipe.MyProperty; }
    }

    public MainViewModel()
    {
        Messenger.Default.Register<Model.Recipe>(this, "NewRecipe", NewRecipe);
        Recipe = new Model.Recipe();
    }

    private void NewRecipe(Recipe obj)
    {
        RaisePropertyChanged("MyProperty");
    }
private Model.Recipe\u Recipe;
公共模式。配方
{
获取{return\u recipe;}
set{if(set(ref _recipe,value)){Messenger.Default.Send(value,“NewRecipe”);}
}
公共字符串MyProperty
{
获取{return“Test”+Recipe.MyProperty;}
}
公共主视图模型()
{
Messenger.Default.Register(这是“NewRecipe”,NewRecipe);
配方=新型号。配方();
}
私有无效新配方(配方obj)
{
RaiseProperty变更(“我的财产”);
}

这种方法的好处是,如果MyProperty位于不同的ViewModel中,它仍然会收到通知,并且它们不会紧密耦合。需要处理配方更改的任何内容都可以注册消息并接收通知,而无需处理每个属性更改事件的巨型方法。

我在MVVM库中使用以下类,以允许属性更改级联到相关属性。如果您认为它对您有用,请随意使用:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace AgentOctal.WpfLib
{
    public class PropertyChangeCascade<T> where T : ObservableObject
    {

        public PropertyChangeCascade(ObservableObject target)
        {
            Target = target;

            Target.PropertyChanged += PropertyChangedHandler;
            _cascadeInfo = new Dictionary<string, List<string>>();
        }

        public ObservableObject Target { get; }
        public bool PreventLoops { get; set; } = false;

        private Dictionary<string, List<string>> _cascadeInfo;

        public PropertyChangeCascade<T> AddCascade(string sourceProperty,
                                                   List<string> targetProperties)
        {
            List<string> cascadeList = null;

            if (!_cascadeInfo.TryGetValue(sourceProperty, out cascadeList))
            {
                cascadeList = new List<string>();
                _cascadeInfo.Add(sourceProperty, cascadeList);
            }

            cascadeList.AddRange(targetProperties);

            return this;
        }

        public PropertyChangeCascade<T> AddCascade(Expression<Func<T, object>> sourceProperty,
                                                   Expression<Func<T, object>> targetProperties)
        {
            string sourceName = null;
            var lambda = (LambdaExpression)sourceProperty;

            if (lambda.Body is MemberExpression expressionS)
            {
                sourceName = expressionS.Member.Name;
            }
            else if (lambda.Body is UnaryExpression unaryExpression)
            {
                sourceName = ((MemberExpression)unaryExpression.Operand).Member.Name;
            }
            else
            {
                throw new ArgumentException("sourceProperty must be a single property", nameof(sourceProperty));
            }

            var targetNames = new List<string>();
            lambda = (LambdaExpression)targetProperties;

            if (lambda.Body is MemberExpression expression)
            {
                targetNames.Add(expression.Member.Name);
            }
            else if (lambda.Body is UnaryExpression unaryExpression)
            {
                targetNames.Add(((MemberExpression)unaryExpression.Operand).Member.Name);
            }
            else if (lambda.Body.NodeType == ExpressionType.New)
            {
                var newExp = (NewExpression)lambda.Body;
                foreach (var exp in newExp.Arguments.Select(argument => argument as MemberExpression))
                {
                    if (exp != null)
                    {
                        var mExp = exp;
                        targetNames.Add(mExp.Member.Name);
                    }
                    else
                    {
                        throw new ArgumentException("Syntax Error: targetProperties has to be an expression " +
                                                    "that returns a new object containing a list of " +
                                                    "properties, e.g.: s => new { s.Property1, s.Property2 }");
                    }
                }
            }
            else
            {
                throw new ArgumentException("Syntax Error: targetProperties has to be an expression " +
                                            "that returns a new object containing a list of " +
                                            "properties, e.g.: s => new { s.Property1, s.Property2 }");
            }

            return AddCascade(sourceName, targetNames);
        }

        public void Detach()
        {
            Target.PropertyChanged -= PropertyChangedHandler;
        }

        private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            List<string> cascadeList = null;

            if (_cascadeInfo.TryGetValue(e.PropertyName, out cascadeList))
            {
                if (PreventLoops)
                {
                    var cascaded = new HashSet<string>();
                    cascadeList.ForEach(cascadeTo =>
                    {
                        if (!cascaded.Contains(cascadeTo))
                        {
                            cascaded.Add(cascadeTo);
                            Target.RaisePropertyChanged(cascadeTo);
                        }
                    });
                }
                else
                {
                    cascadeList.ForEach(cascadeTo =>
                    {
                        Target.RaisePropertyChanged(cascadeTo);
                    });
                }
            }
        }
    }
}
构造函数中的行将
Name
属性的更改级联到
DoubleName
TripleName
属性。默认情况下,出于性能原因,它不会检查级联中的循环,因此它依赖于您不创建它们。您可以选择将级联上的
PreventLoops
设置为
true
,并确保每个属性只引发一次
PropertyChanged

但是我想把它颠倒过来,这样食谱就不知道了 关于标题。它只是简单地广播它已加载,然后是任何内容 需要对加载的配方做出反应的将在 隔离

听起来Steven的Cleary计算属性正是您所需要的:

我已经在网站上更详细地回答了类似的问题


这个图书馆很神奇。事实上,我会为任何MVVM项目推荐它,无论它是新的还是旧的,增量采用计算属性并享受即时好处都是微不足道的。

Err。。你听说过绑定吗?InotifyProperty是否已更改?它们一起给出了“标题属性订阅实例属性,以便加载实例时标题自动更新”的效果我希望您不介意,但我稍微编辑了您问题的标题,使其更符合我们的指导原则,与你的实际问题更接近一点。对我来说,这更像是一个评论,而不是一个真实的答案。你给出的另一个答案非常有建设性:不过,这个答案可以通过一些解释加以改进。即使你引用了必要的文本,这也会改善你的答案,只要它确实回答了问题。我同意你对回复的评估,并感谢建设性的反馈。但是,我刚刚开始投稿,没有添加评论的权限,因此这是我就这个问题提供意见的唯一方法。
class CascadingPropertyVM : ViewModel
{
    public CascadingPropertyVM()
    {
        new PropertyChangeCascade<CascadingPropertyVM>(this)
            .AddCascade(s => s.Name,
            t => new { t.DoubleName, t.TripleName });
    }

    private string _name;
    public string Name
    {
        get => _name;
        set => SetValue(ref _name, value);
    }

    public string DoubleName => $"{Name} {Name}";
    public string TripleName => $"{Name} {Name} {Name}";
}