Gui не обновляется, если свойство другого класса изменилось - PullRequest
0 голосов
/ 09 мая 2018

Я хочу привязать некоторые свойства (FooClass.FooString) пользовательского класса (FooClass) к моему MainWindow. Ниже ( Рабочее известное поведение ) - рабочее решение по умолчанию, если привязать некоторые данные к графическому интерфейсу.

Что я хочу сделать, это во втором блоке кода ( Не работает, но желаемое поведение ). Предоставьте properties другого class объекта to the gui and update it. **Problem**: The TestString is not getting updated (on the gui, code behind works). The PropertyChanged событие is also null` (не подписан?!).

Это неправильный способ связывания данных?

Если я связываю полный FooClass object с графическим интерфейсом и устанавливаю Path (из TextBlock) в Foo.FooString, графический интерфейс и string обновляются. Но я не хочу делать это так.

Это способ, как это решить?


рабочее известное поведение

public partial class MainWindow : Window
{
    public FooClass Foo { get; } = new FooClass();

    public MainWindow()
    {
        DataContext = this;
        InitializeComponent();

        Loaded += _OnLoaded;
    }

    private async void _OnLoaded(object sender, RoutedEventArgs e)
    {
        await Task.Delay(1000);
        Foo.ChangeTheProperty();
    }
}

public class FooClass : INotifyPropertyChanged
{
    public string FooString
    {
        get => _FooString;
        set
        {
            if (_FooString == value) return;
            _FooString = value;
            OnPropertyChanged();
        }
    }
    private string _FooString = "empty";

    public void ChangeTheProperty()
    {
        FooString = "Loaded";
    }

    // ##############################################################################################################################
    // PropertyChanged
    // ##############################################################################################################################

    #region PropertyChanged

    /// <summary>
    /// The PropertyChanged Eventhandler
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raise/invoke the propertyChanged event!
    /// </summary>
    /// <param name="propertyName"></param>        
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

MainWindow.xaml

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:MainWindow}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBlock Text="{Binding Path=Foo.FooString}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Grid>
</Window>

Не работает, но желаемое поведение

public partial class MainWindow : Window
{
    public string TestString => _Foo.FooString;


    private readonly FooClass _Foo;

    public MainWindow()
    {
        _Foo = new FooClass();
        DataContext = this;
        InitializeComponent();

        Loaded += _OnLoaded;
    }

    private async void _OnLoaded(object sender, RoutedEventArgs e)
    {
        await Task.Delay(1000);
        _Foo.ChangeTheProperty();
    }
}

public class FooClass : INotifyPropertyChanged
{
    public string FooString
    {
        get => _FooString;
        set
        {
            if (_FooString == value) return;
            _FooString = value;
            OnPropertyChanged();
        }
    }
    private string _FooString = "empty";

    public void ChangeTheProperty()
    {
        FooString = "Loaded";
    }

    // ##############################################################################################################################
    // PropertyChanged
    // ##############################################################################################################################

    #region PropertyChanged

    /// <summary>
    /// The PropertyChanged Eventhandler
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raise/invoke the propertyChanged event!
    /// </summary>
    /// <param name="propertyName"></param>        
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

MainWindow.xaml

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:MainWindow}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBlock Text="{Binding Path=TestString}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Grid>
</Window>

Решение 1

Подпишитесь на Foo.PropertyChanged event и направьте его на MainWindow.PropertyChanged.

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public FooClass Foo { get; } = new FooClass();

    public MainWindow()
    {
        Foo.PropertyChanged += (sender, args) => OnPropertyChanged(args.PropertyName);
        DataContext = this;
        InitializeComponent();

        Loaded += _OnLoaded;
    }

    private async void _OnLoaded(object sender, RoutedEventArgs e)
    {
        await Task.Delay(1000);
        Foo.ChangeTheProperty();
    }


    // ##############################################################################################################################
    // PropertyChanged
    // ##############################################################################################################################

    #region PropertyChanged

    /// <summary>
    /// The PropertyChanged Eventhandler
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raise/invoke the propertyChanged event!
    /// </summary>
    /// <param name="propertyName"></param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

}

1 Ответ

0 голосов
/ 09 мая 2018

Возможно, я не совсем понял, что вы хотите, но вот рабочий пример привязки данных, который несколько похож на ваш пример.

Два основных изменения были:

  1. Установите текстовый текст для виртуальной машины, а не код позади
  2. На самом деле дать OnPropertyChanged аргумент, необходимый для корректного запуска обновления, имя свойства.

Результат: enter image description here

MainWindow.xaml

<Window x:Class="ListViewColor.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <TextBlock Text="{Binding Foo.FooString}" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Aqua"/>
    </Grid>
</Window>

MainWindow.xaml.cs

namespace ListViewColor
{
    public partial class MainWindow : Window
    {
        public FooClass Foo { get; } = new FooClass();

        public MainWindow()
        {
            DataContext = this;
            InitializeComponent();
            Loaded += _OnLoaded;
        }

        private async void _OnLoaded(object sender, RoutedEventArgs e)
        {
            await Task.Delay(1000);
            Foo.ChangeTheProperty();
        }
    }
}

FooClass.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class FooClass : INotifyPropertyChanged
{
    private string _FooString = "Empty";
    public string FooString
    {
        get
        {
            return _FooString;
        }
        set
        {
            if (_FooString == value) return;
            _FooString = value;
            OnPropertyChanged();
        }
    }

    public void ChangeTheProperty()
    {
        FooString = "Loaded";
    }

    #region PropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...