【问题标题】:How come my UI wont update and databind when I set the DataContext to a object当我将 DataContext 设置为对象时,为什么我的 UI 不会更新和数据绑定
【发布时间】:2018-05-16 11:01:02
【问题描述】:

所以在这一点上我很困惑。我现在绑定它的方式非常完美,但我想使用多个类进行数据绑定。所以我现在所拥有的(工作版本)是这样的。在我的 MainWindow 中,我将 DataContext 设置为我的类的一个新实例,名为 Server

public MainWindow()
{
    InitializeComponent();
    DataContext = server;
}

我已经将一些属性绑定到几个控件,像这样

<ScrollViewer VerticalScrollBarVisibility="Visible" Name="Scroller" Margin="266,95,10,192" Background="#1f1f1f">
    <StackPanel>
    <ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                <TextBlock Text="{Binding Text}"
                               Foreground="{Binding Foreground}"
                               Name="SavedBlocks" FontFamily="Consolas"
                               LayoutUpdated="SavedBlocks_LayoutUpdated" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>
</ScrollViewer>

这就是我的Server 课程

public class Server : INotifyPropertyChanged
{
    public string consoleInput = string.Empty;
    public ObservableCollection<Message> consoleOutput = new ObservableCollection<Message>();

    public Server()
    {

    }

    public string ConsoleInput
    {
        get => consoleInput;
        set
        {
            consoleInput = value;
            OnPropertyChanged("ConsoleInput");
        }
    }


private void OKDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (ServerIsRunning)
            {
                if (!string.IsNullOrEmpty(e.Data))

                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        if (e.Data.Contains("ERROR"))
                        {
                            ConsoleOutput.Add(new Message { Text = e.Data, Foreground = ERRORBrush });
                            ConsoleInput = string.Empty;
                            return;
                        }

                        if (e.Data.Contains("WARN"))
                        {
                            ConsoleOutput.Add(new Message { Text = e.Data, Foreground = WARNINGBrush });
                            ConsoleInput = string.Empty;
                            return;
                        }

                        ConsoleOutput.Add(new Message { Text = e.Data, Foreground = OKBrush });
                        ConsoleInput = string.Empty;
                    });
            }
        }


    public ObservableCollection<Message> ConsoleOutput
    {
        get => consoleOutput;
        set
        {
            consoleOutput = value;
            OnPropertyChanged("ConsoleOutput");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (null != PropertyChanged)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

如您所见,ObservableCollection 具有 Message 类型,即此类

public class Message : INotifyPropertyChanged
{
    public Message()
    {
        _foreground = Brushes.Green;
    }
    private string _text;
    public string Text
    {
        get { return _text; }
        set { _text = value; OnPropertyChanged("Text"); }
    }

    private Brush _foreground;
    public Brush Foreground
    {
        get { return _foreground; }
        set
        {
            _foreground = value;
            OnPropertyChanged("Foreground");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged(string propertyName)
    {
        if (null != PropertyChanged)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

我遇到的问题是当我尝试创建一个 MasterViewModel 类时,该类将包含所有其他类的属性

public class MasterViewModel
{
    public Server ServerViewModel { get; } = new Server();
    public Player PlayerViewModel { get; } = new Player();
    public Message MessageViewModel { get; } = new Message();
}

然后我要设置属性如下

以及更改 DataContext

public MainWindow()
{
    InitializeComponent();
    DataContext = new MasterViewModel();
}

XAML

<ItemsControl ItemsSource="{Binding ServerViewModel.ConsoleOutput, Mode=OneWay}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
        <TextBlock Text="{Binding MessageViewModel.Text}"
                       Foreground="{Binding MessageViewModel.Foreground}"
                       Name="SavedBlocks" FontFamily="Consolas"
                       LayoutUpdated="SavedBlocks_LayoutUpdated" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

但是突然间它停止了工作,它不会通知 UI 并且 UI 根本不会更新。 我在这里错过了一些小东西吗?因为我一辈子都找不到这里的问题。

新绑定

static Server server = new Server();
        static Player player = new Player();
        static Message message = new Message();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MasterViewModel()
            {
                ServerViewModel = server,
                PlayerViewModel = player,
                MessageViewModel = message,
            };
        }

【问题讨论】:

  • ItemsControl 中项目容器的 DataContext(以及因此 DataTemplate 中的所有元素)自动设置为相应的项目对象,即 ItemsSource 集合中的相应元素。这意味着 DataTemplate 中 TextBlock 的 DataContext 已经包含一个 Message 对象。然后你绑定它的属性,例如Text="{Binding Text}".
  • 我尝试再次恢复它们,只是更改了 DataContext 但似乎没有任何改变
  • 为什么以及在哪里“更改 DataContext”?这似乎没有意义。
  • 应该的。你一定做错了什么。请注意,在 ItemsSource Binding 上设置 Mode=OneWay 是多余的。
  • 您查看的服务器实例可能与 MasterViewModel 的 ServerViewModel 属性中的实例不同。您的代码中似乎有一个server 变量。将其分配给 ServerViewModel 属性。

标签: c# .net wpf mvvm data-binding


【解决方案1】:

您不应该更改 ItemTemplate 中的绑定。 ObservableCollection&lt;Message&gt; ConsoleOutput 仍然包含具有 Text 和 Foreground 属性的 Message 对象。绑定路径MessageViewModel.Text对Message无效,恢复为{Binding Text}{Binding Foreground}

【讨论】:

  • 我尝试还原它,但似乎没有太大区别
  • server 变量是什么?请尝试DataContext = new MasterViewModel { ServerViewModel = server; }
  • 服务器变量是这个static Server server = new Server();我会试试你的
  • 我也不能设置这个值,因为它是只读的,所以它是多余的public Server ServerViewModel { get; } = new Server();
  • 我担心可能有一些其他代码适用于server 而不适用于MasterViewModel.ServerViewModel
【解决方案2】:

您需要将MasterViewModel 类的ServerViewModel 属性设置为您之前拥有的server 对象:

public MainWindow()
{
    InitializeComponent();
    DataContext = new MasterViewModel() { ServerViewModel = server };
}

这意味着你应该给MasterViewModel的属性添加一个setter:

public class MasterViewModel
{
    public Server ServerViewModel { get; set; }
    public Player PlayerViewModel { get; set; }
    public Message MessageViewModel { get; set; }
}

确保同时设置PlayerViewModelMessage 属性。

【讨论】:

  • 那不会让我绑定到任何其他类吧?由于我们将 DataContext 设置为该类的实例,该解决方案是如何工作的?
  • 我想我可以做这样的事情..(我会将它添加到我的问题的底部)但这看起来很糟糕而且感觉多余,因为我在我的主要方法中创建了新实例当我拥有包含所有这些的 MasteViewModel 时
  • MasterViewModel 不包含任何实例。它是一个定义 properties 的类,您需要将其设置为一些实例以进行初始化。如果你再看看我的回答,你会发现里面没有 new 关键字。
【解决方案3】:

这就是我用一个小样本做的事情

public class MasterVM 
    {
        public ServerVM Server { get; set; }
    }

    public class ServerVM : INotifyPropertyChanged

    {
        private string serverName;
        public string ServerName {
            get
            {
                return serverName;
            }
            set
            {
                serverName = value;
                OnPropertyChanged("ServerName");        
            }

        }

        private void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

这里是 xaml

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Click="Button_Click">Change Server NAme</Button>

        <Label Grid.Row="1" Content="{Binding Server.ServerName}" />

    </Grid>

它就像一个魅力。

我可以建议您在每个类中设置一个 _id 字段并初始化为新的 GUID,并使用名为“Snoop”的工具来检查这些对象的运行时 ID。

【讨论】:

    猜你喜欢
    • 2014-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 2011-04-21
    • 2019-04-12
    相关资源
    最近更新 更多