【问题标题】:WPF C# UI update a property from a different classWPF C# UI 更新来自不同类的属性
【发布时间】:2018-07-24 10:24:33
【问题描述】:

我创建了一个简单的示例来说明我的问题。我不能操纵另一个类的属性。我创建了两个类,一个有一个命令和一个属性。第二个具有相同的命令,但引用了第一个类中的属性。

具有属性和命令的类按预期工作。另一个没有,但我不明白为什么。我对此很陌生,我显然在概念上遗漏了一些东西……非常感谢您的解释。

<Window x:Class="TestProperties.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TestProperties"
    xmlns:mvvm="clr-namespace:TestProperties.MVVM"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" Background="Black">
<Window.Resources>
    <mvvm:Class1 x:Key="Class1"/>
    <mvvm:Class2 x:Key="Class2"/>
</Window.Resources>

<StackPanel DataContext="{Binding Mode=OneWay, Source={StaticResource Class1}}">

    <TextBox Height="33" Width="100" Margin="0 33 0 0"
             Text="{Binding MyProperty, UpdateSourceTrigger=PropertyChanged}"/>

    <Button Height="33" Width="100" Margin="33" Content="Class 1 - change value" 
            Command="{Binding ChangeValue, Mode=OneWay, Source={StaticResource Class1}}"/>

    <Button Height="33" Width="100" Margin="33" Content="Class 2 - change value" 
            Command="{Binding ChangeValue, Mode=OneWay, Source={StaticResource Class2}}"/>

</StackPanel>

namespace TestProperties.MVVM
{
class Class1 : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
            Debug.WriteLine($"Class1 NOTIFY PROPERTY CHANGED! {info.ToString()}");
        }
    }

    private int myVar;
    public int MyProperty
    {
        get { return myVar; }
        set { myVar = value; NotifyPropertyChanged("MyProperty"); }
    }

    private ICommand _ChangeValue;
    public ICommand ChangeValue
    {
        get
        {
            if (_ChangeValue == null)
            {
                _ChangeValue = new RelayCommand<object> CanExecute_ChangeValue);
            }

            return _ChangeValue;
        }
    }
    public bool CanExecute_ChangeValue(object parameter)
    {
        return true;
    }
    public void Execute_ChangeValue(object parameter)
    {
        Console.WriteLine($"Execute_ChangeValue");

        MyProperty = 5;

        Console.WriteLine($"c1.MyProperty: {MyProperty}");
    }
}

class Class2
{
    private ICommand _ChangeValue;
    public ICommand ChangeValue
    {
        get
        {
            if (_ChangeValue == null)
            {
                _ChangeValue = new RelayCommand<object>(Execute_ChangeValue, CanExecute_ChangeValue);
            }

            return _ChangeValue;
        }
    }
    public bool CanExecute_ChangeValue(object parameter)
    {
        return true;
    }
    public void Execute_ChangeValue(object parameter)
    {
        Console.WriteLine($"Execute_ChangeValue");

        Class1 c1 = new Class1();
        c1.MyProperty = 5;

        Console.WriteLine($"c1.MyProperty: {c1.MyProperty }");
    }
}

public class RelayCommand<T> : ICommand
{
    #region Fields
    readonly Action<T> _execute = null;

    readonly Predicate<T> _canExecute = null;
    #endregion // Fields

    #region Constructors
    public RelayCommand(Action<T> execute) : this(execute, null)
    {

    }

    public RelayCommand(Action<T> execute, Predicate<T> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members
    //[DebuggerStepThrough]

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute((T)parameter);
    }


    public event EventHandler CanExecuteChanged
    {
        add
        { CommandManager.RequerySuggested += value; }

        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }
    #endregion // ICommand Members
}

【问题讨论】:

    标签: c# wpf class properties


    【解决方案1】:

    您正在更改该类的全新实例中的值。代码的其余部分不知道存在一个实例,因此当然无法观察新的属性值。命令应该使用您在 XAML 中声明的 Class1 对象,而不是新创建的 Class1 实例。

    很难知道实际代码中的最佳解决方案是什么。但是,在您发布的代码中,鉴于您似乎想在 XAML 中声明这些类的实例,您可以更改 Class2 以拥有一个可以设置为您关心的 Class1 实例的属性,并且然后在命令中使用该实例:

    class Class2
    {
        private ICommand _ChangeValue;
        public ICommand ChangeValue
        {
            get
            {
                if (_ChangeValue == null)
                {
                    _ChangeValue = new RelayCommand<object>(Execute_ChangeValue, CanExecute_ChangeValue);
                }
    
                return _ChangeValue;
            }
        }
    
        // NOTE: not observable, due to lack of INotifyPropertyChanged.
        // It's fine in this scenario, but don't expect to be able to change
        // this value and have bindings update automatically.
        private Class1 _class1;
        public Class1 Class1
        {
            get { return _class1; }
            set { _class1 = value; }
        }
    
        public bool CanExecute_ChangeValue(object parameter)
        {
            return true;
        }
    
        public void Execute_ChangeValue(object parameter)
        {
            Console.WriteLine($"Execute_ChangeValue");
    
            Class1.MyProperty = 5;
    
            Console.WriteLine($"Class1.MyProperty: {Class1.MyProperty }");
        }
    }
    

    然后在 XAML 中,类似这样的内容(请注意,所有 Class1 引用都相对于 Class2):

    <Window.Resources>
        <mvvm:Class2 x:Key="Class2">
            <mvvm:Class2.Class1>
                <mvvm:Class1 x:Key="Class1"/>
            </mvvm:Class2.Class1>
        </mvvm:Class2>
    </Window.Resources>
    
    <StackPanel DataContext="{Binding Class1, Mode=OneWay, Source={StaticResource Class2}}">
    
        <TextBox Height="33" Width="100" Margin="0 33 0 0"
                 Text="{Binding MyProperty, UpdateSourceTrigger=PropertyChanged}"/>
    
        <Button Height="33" Width="100" Margin="33" Content="Class 1 - change value" 
                Command="{Binding Class1.ChangeValue, Mode=OneWay, Source={StaticResource Class2}}"/>
    
        <Button Height="33" Width="100" Margin="33" Content="Class 2 - change value" 
                Command="{Binding ChangeValue, Mode=OneWay, Source={StaticResource Class2}}"/>
    
    </StackPanel>
    

    在您的真实代码中,您可能希望通过其他方式将Class1 引用与Class2 对象共享。或者,首先不要重复该命令。坦率地说,在我看来,有两个不同的命令来执行相同的操作,这似乎有点奇怪。问题中没有足够的上下文来知道这是否真的需要,如果不需要,人们会改变什么来解决它。但这当然是您应该仔细检查以确保您确实需要这样做的事情。

    【讨论】:

    • 谢谢。是的,我真的不想那样做。那只是为了说明问题。我想要做的是从另一个类中的命令影响一个类中的属性并更新 UI。几乎就好像该属性是“静态的”,但这似乎也不起作用。我最好的解决方案是将所有内容放入一个巨大的类中,这不是我的第一选择,但也许应该这样做?感谢您的帮助!
    • 没有足够的上下文来知道static 是否是一种合适的方法,或者将所有内容都放在一个巨大的类中。就像我说的,你显然有两个不同的命令做同样的事情,这是一种代码味道。恕我直言,您应该首先努力消除设计的那部分,然后所有其他问题都消失了。我只能使用您原始问题中提供的上下文...上面的答案直接解决了这个问题。
    猜你喜欢
    • 2017-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多