C# WPF MVVM:数据网格的显示视图';s SelectedItem

C# WPF MVVM:数据网格的显示视图';s SelectedItem,c#,wpf,xaml,mvvm,datagrid,C#,Wpf,Xaml,Mvvm,Datagrid,我是MVVM和WPF的新手,对这个问题已经完全挂断了一段时间。我试图在DataGrid中显示基于SelectedItem的视图(UserControl)。UserControl使用构造函数中的数据集呈现,但从不更新 我真的很感激在这方面有经验的人的一些见解。我尝试通过setUpdateCallback添加中介,现在datagrid中的第一行使用我单击的其他行的值进行更新,但这显然不是我想要的,我需要在datagrid之外的单独客户端视图中进行这些更新 ClientPanel.xaml <

我是MVVM和WPF的新手,对这个问题已经完全挂断了一段时间。我试图在DataGrid中显示基于SelectedItem的视图(UserControl)。UserControl使用构造函数中的数据集呈现,但从不更新

我真的很感激在这方面有经验的人的一些见解。我尝试通过setUpdateCallback添加中介,现在datagrid中的第一行使用我单击的其他行的值进行更新,但这显然不是我想要的,我需要在datagrid之外的单独客户端视图中进行这些更新

ClientPanel.xaml

<UserControl x:Class="B2BNet.View.ClientPanel" 
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:VM="clr-namespace:B2BNet.ViewModel"
         xmlns:V="clr-namespace:B2BNet.View"
         xmlns:local="clr-namespace:B2BNet"
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
         mc:Ignorable="d">
<Grid>
    <Grid.DataContext>
        <VM:ClientPanel/>
    </Grid.DataContext>
    <TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="43" Width="280" Text="{Binding Title}" FontSize="36" FontFamily="Global Monospace"/>
    <DataGrid AutoGenerateColumns="False" x:Name="dataGrid" HorizontalAlignment="Left" Margin="10,60,0,10" VerticalAlignment="Top" ItemsSource="{Binding Clients}" SelectedItem="{Binding currentClient}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding name}"></DataGridTextColumn>
            <DataGridTextColumn Header="Active" Binding="{Binding active}"></DataGridTextColumn>
            <DataGridTextColumn Header="Status" Binding="{Binding status}"></DataGridTextColumn>
        </DataGrid.Columns>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <i:InvokeCommandAction Command="{Binding clientSelectionChanged_command}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </DataGrid>
    <V:Client HorizontalAlignment="Left" Margin="163,58,-140,0" VerticalAlignment="Top" Content="{Binding currentClient}" Height="97" Width="201"/>
</Grid>
<UserControl x:Class="B2BNet.View.Client"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:VM="clr-namespace:B2BNet.ViewModel"
         xmlns:local="clr-namespace:B2BNet"
         mc:Ignorable="d">
<Grid>
    <Grid.DataContext>
        <VM:Client/>
    </Grid.DataContext>
    <Label x:Name="name" Content="Name:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
    <Label x:Name="active" Content="Active:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="9,41,0,0"/>
    <Label x:Name="status" Content="Status:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,72,0,0"/>
    <Label x:Name="namevalue" HorizontalAlignment="Left" Margin="59,10,0,0" VerticalAlignment="Top" Width="200" Height="26" Content="{Binding name}"/>
    <Label x:Name="activevalue" HorizontalAlignment="Left" Margin="59,41,0,0" VerticalAlignment="Top" Width="200" Height="26" Content="{Binding active}"/>
    <Label x:Name="statusvalue" HorizontalAlignment="Left" Margin="60,67,-1,0" VerticalAlignment="Top" Width="200" Height="26" Content="{Binding status}"/>

</Grid>
非常基本的MVVM框架

可观察对象.cs

using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using B2BNet.MVVM;

namespace B2BNet.ViewModel
{
    public class ClientPanel : ObservableObject
    {
        public string Title { get; set; }

        private Client _currentClient;
        public Client currentClient
        {
            get
            {
                return _currentClient;
            }
            set
            {
                _currentClient = value;
                RaisePropertyChangedEvent( "currentClient" );
                Utility.print("currentClient Changed: " + _currentClient.name);
                Mediator.Instance.Notify(ViewModelMessages.UpdateClientViews, currentClient);
            }
        }

        private ObservableCollection<Client> _Clients;
        public ObservableCollection<Client> Clients
        {
            get
            {
                return _Clients;
            }
            set
            {
                _Clients = value;
                RaisePropertyChangedEvent("Clients");
                Utility.print("Clients Changed");
            }
        }


        public ClientPanel()
        {
            Title = "Clients";
            Clients = new ObservableCollection<Client>();

            for ( int i = 0; i < 10; i++ )
            {
                Clients.Add( new Client { name = "Client-" + i, status = "Current", active = true } );
            }
            currentClient = Clients.First();
            currentClient.setUpdateCallback();
        }

        ////////////
        //COMMANDS//
        ////////////
        public ICommand clientSelectionChanged_command
        {
            get { return new DelegateCommand( clientSelectionChanged ); }
        }
        private void clientSelectionChanged( object parameter )
        {
            B2BNet.Utility.print("clientSelectionChanged");
        }
    }
}
using System;
using B2BNet.MVVM;

namespace B2BNet.ViewModel
{
    public class Client : ObservableObject
    {

        private String _name;
        public String name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChangedEvent( "name" );
                Utility.print("Client.name changed: " + value );
            }
        }

        private Boolean _active;
        public Boolean active
        {
            get
            {
                return _active;
            }
            set
            {
                _active = value;
                RaisePropertyChangedEvent( "active" );
                Utility.print("Client.active changed" + value );
            }
        }

        private String _status;
        public String status
        {
            get
            {
                return _status;
            }
            set
            {
                _status = value;
                RaisePropertyChangedEvent( "status" );
                Utility.print("Client.status changed" + value );
            }
        }

        public Client()
        {
            name = "Set in Client Constuctor";
            status = "Set in Client Constructor";
        }

        public void setUpdateCallback()
        {
            ////////////
            //Mediator//
            ////////////
            //Register a callback with the Mediator for Client
            Mediator.Instance.Register(
                (Object o) =>
                {
                    Client client = (Client)o;
                    name = client.name;
                    active = client.active;
                    status = client.status;
                }, B2BNet.MVVM.ViewModelMessages.UpdateClientViews
            );
        }
   }
}
using System.ComponentModel;

namespace B2BNet.MVVM
{
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChangedEvent( string propertyName )
        {
            var handler = PropertyChanged;
            if ( handler != null )
                handler( this, new PropertyChangedEventArgs( propertyName ) );
        }
    }
}
using System;
using B2BNet.lib;

namespace B2BNet.MVVM
{
    /// <summary>
    /// Available cross ViewModel messages
    /// </summary>
    public enum ViewModelMessages { UpdateClientViews = 1,
                                    DebugUpdated = 2
                                   };


    public sealed class Mediator
    {
        #region Data
        static readonly Mediator instance = new Mediator();
        private volatile object locker = new object();

        MultiDictionary<ViewModelMessages, Action<Object>> internalList
            = new MultiDictionary<ViewModelMessages, Action<Object>>();
        #endregion

        #region Ctor
        //CTORs
        static Mediator()
        {


        }

        private Mediator()
        {

        }
        #endregion

        #region Public Properties

        /// <summary>
        /// The singleton instance
        /// </summary>
        public static Mediator Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Registers a callback to a specific message
        /// </summary>
        /// <param name="callback">The callback to use 
        /// when the message it seen</param>
        /// <param name="message">The message to 
        /// register to</param>
        public void Register(Action<Object> callback, 
            ViewModelMessages message)
        {
            internalList.AddValue(message, callback);
        }


        /// <summary>
        /// Notify all callbacks that are registed to the specific message
        /// </summary>
        /// <param name="message">The message for the notify by</param>
        /// <param name="args">The arguments for the message</param>
        public void Notify(ViewModelMessages message, 
            object args)
        {
            if (internalList.ContainsKey(message))
            {
                //forward the message to all listeners
                foreach (Action<object> callback in 
                    internalList[message])
                        callback(args);
            }
        }
        #endregion    
    }
}
using System;
using System.Windows.Input;

namespace B2BNet.MVVM
{
    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _action;

        public DelegateCommand( Action<object> action )
        {
            _action = action;
        }

        public void Execute( object parameter )
        {
            _action( parameter );
        }

        public bool CanExecute( object parameter )
        {
            return true;
        }

        #pragma warning disable 67
        public event EventHandler CanExecuteChanged;
        #pragma warning restore 67
    }
}
Mediator.cs

using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using B2BNet.MVVM;

namespace B2BNet.ViewModel
{
    public class ClientPanel : ObservableObject
    {
        public string Title { get; set; }

        private Client _currentClient;
        public Client currentClient
        {
            get
            {
                return _currentClient;
            }
            set
            {
                _currentClient = value;
                RaisePropertyChangedEvent( "currentClient" );
                Utility.print("currentClient Changed: " + _currentClient.name);
                Mediator.Instance.Notify(ViewModelMessages.UpdateClientViews, currentClient);
            }
        }

        private ObservableCollection<Client> _Clients;
        public ObservableCollection<Client> Clients
        {
            get
            {
                return _Clients;
            }
            set
            {
                _Clients = value;
                RaisePropertyChangedEvent("Clients");
                Utility.print("Clients Changed");
            }
        }


        public ClientPanel()
        {
            Title = "Clients";
            Clients = new ObservableCollection<Client>();

            for ( int i = 0; i < 10; i++ )
            {
                Clients.Add( new Client { name = "Client-" + i, status = "Current", active = true } );
            }
            currentClient = Clients.First();
            currentClient.setUpdateCallback();
        }

        ////////////
        //COMMANDS//
        ////////////
        public ICommand clientSelectionChanged_command
        {
            get { return new DelegateCommand( clientSelectionChanged ); }
        }
        private void clientSelectionChanged( object parameter )
        {
            B2BNet.Utility.print("clientSelectionChanged");
        }
    }
}
using System;
using B2BNet.MVVM;

namespace B2BNet.ViewModel
{
    public class Client : ObservableObject
    {

        private String _name;
        public String name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChangedEvent( "name" );
                Utility.print("Client.name changed: " + value );
            }
        }

        private Boolean _active;
        public Boolean active
        {
            get
            {
                return _active;
            }
            set
            {
                _active = value;
                RaisePropertyChangedEvent( "active" );
                Utility.print("Client.active changed" + value );
            }
        }

        private String _status;
        public String status
        {
            get
            {
                return _status;
            }
            set
            {
                _status = value;
                RaisePropertyChangedEvent( "status" );
                Utility.print("Client.status changed" + value );
            }
        }

        public Client()
        {
            name = "Set in Client Constuctor";
            status = "Set in Client Constructor";
        }

        public void setUpdateCallback()
        {
            ////////////
            //Mediator//
            ////////////
            //Register a callback with the Mediator for Client
            Mediator.Instance.Register(
                (Object o) =>
                {
                    Client client = (Client)o;
                    name = client.name;
                    active = client.active;
                    status = client.status;
                }, B2BNet.MVVM.ViewModelMessages.UpdateClientViews
            );
        }
   }
}
using System.ComponentModel;

namespace B2BNet.MVVM
{
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChangedEvent( string propertyName )
        {
            var handler = PropertyChanged;
            if ( handler != null )
                handler( this, new PropertyChangedEventArgs( propertyName ) );
        }
    }
}
using System;
using B2BNet.lib;

namespace B2BNet.MVVM
{
    /// <summary>
    /// Available cross ViewModel messages
    /// </summary>
    public enum ViewModelMessages { UpdateClientViews = 1,
                                    DebugUpdated = 2
                                   };


    public sealed class Mediator
    {
        #region Data
        static readonly Mediator instance = new Mediator();
        private volatile object locker = new object();

        MultiDictionary<ViewModelMessages, Action<Object>> internalList
            = new MultiDictionary<ViewModelMessages, Action<Object>>();
        #endregion

        #region Ctor
        //CTORs
        static Mediator()
        {


        }

        private Mediator()
        {

        }
        #endregion

        #region Public Properties

        /// <summary>
        /// The singleton instance
        /// </summary>
        public static Mediator Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Registers a callback to a specific message
        /// </summary>
        /// <param name="callback">The callback to use 
        /// when the message it seen</param>
        /// <param name="message">The message to 
        /// register to</param>
        public void Register(Action<Object> callback, 
            ViewModelMessages message)
        {
            internalList.AddValue(message, callback);
        }


        /// <summary>
        /// Notify all callbacks that are registed to the specific message
        /// </summary>
        /// <param name="message">The message for the notify by</param>
        /// <param name="args">The arguments for the message</param>
        public void Notify(ViewModelMessages message, 
            object args)
        {
            if (internalList.ContainsKey(message))
            {
                //forward the message to all listeners
                foreach (Action<object> callback in 
                    internalList[message])
                        callback(args);
            }
        }
        #endregion    
    }
}
using System;
using System.Windows.Input;

namespace B2BNet.MVVM
{
    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _action;

        public DelegateCommand( Action<object> action )
        {
            _action = action;
        }

        public void Execute( object parameter )
        {
            _action( parameter );
        }

        public bool CanExecute( object parameter )
        {
            return true;
        }

        #pragma warning disable 67
        public event EventHandler CanExecuteChanged;
        #pragma warning restore 67
    }
}
使用系统;
使用B2BNet.lib;
命名空间B2BNet.MVVM
{
/// 
///可用的交叉视图模型消息
/// 
公共枚举ViewModelMessages{UpdateClientViews=1,
DebugUpdated=2
};
公共密封类调解员
{
#区域数据
静态只读中介实例=new Mediator();
私有易失性对象锁定器=新对象();
多字典内部列表
=新的多字典();
#端区
#区域导体
//演员
静态介体()
{
}
私人调解人()
{
}
#端区
#区域公共财产
/// 
///单例实例
/// 
公共静态中介实例
{
得到
{
返回实例;
}
}
#端区
#区域公共方法
/// 
///注册对特定消息的回调
/// 
///要使用的回调函数
///当它看到消息时
///传达给
///注册到
公共无效寄存器(操作回调,
ViewModelMessages(消息)
{
AddValue(消息、回调);
}
/// 
///通知注册到特定消息的所有回调
/// 
///通知人的消息
///消息的参数
公共无效通知(ViewModelMessages消息,
对象参数)
{
if(internalList.ContainsKey(消息))
{
//将消息转发给所有侦听器
foreach(中的操作回调)
内部列表[消息])
回调(args);
}
}
#端区
}
}
DelegateCommand.cs

using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using B2BNet.MVVM;

namespace B2BNet.ViewModel
{
    public class ClientPanel : ObservableObject
    {
        public string Title { get; set; }

        private Client _currentClient;
        public Client currentClient
        {
            get
            {
                return _currentClient;
            }
            set
            {
                _currentClient = value;
                RaisePropertyChangedEvent( "currentClient" );
                Utility.print("currentClient Changed: " + _currentClient.name);
                Mediator.Instance.Notify(ViewModelMessages.UpdateClientViews, currentClient);
            }
        }

        private ObservableCollection<Client> _Clients;
        public ObservableCollection<Client> Clients
        {
            get
            {
                return _Clients;
            }
            set
            {
                _Clients = value;
                RaisePropertyChangedEvent("Clients");
                Utility.print("Clients Changed");
            }
        }


        public ClientPanel()
        {
            Title = "Clients";
            Clients = new ObservableCollection<Client>();

            for ( int i = 0; i < 10; i++ )
            {
                Clients.Add( new Client { name = "Client-" + i, status = "Current", active = true } );
            }
            currentClient = Clients.First();
            currentClient.setUpdateCallback();
        }

        ////////////
        //COMMANDS//
        ////////////
        public ICommand clientSelectionChanged_command
        {
            get { return new DelegateCommand( clientSelectionChanged ); }
        }
        private void clientSelectionChanged( object parameter )
        {
            B2BNet.Utility.print("clientSelectionChanged");
        }
    }
}
using System;
using B2BNet.MVVM;

namespace B2BNet.ViewModel
{
    public class Client : ObservableObject
    {

        private String _name;
        public String name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChangedEvent( "name" );
                Utility.print("Client.name changed: " + value );
            }
        }

        private Boolean _active;
        public Boolean active
        {
            get
            {
                return _active;
            }
            set
            {
                _active = value;
                RaisePropertyChangedEvent( "active" );
                Utility.print("Client.active changed" + value );
            }
        }

        private String _status;
        public String status
        {
            get
            {
                return _status;
            }
            set
            {
                _status = value;
                RaisePropertyChangedEvent( "status" );
                Utility.print("Client.status changed" + value );
            }
        }

        public Client()
        {
            name = "Set in Client Constuctor";
            status = "Set in Client Constructor";
        }

        public void setUpdateCallback()
        {
            ////////////
            //Mediator//
            ////////////
            //Register a callback with the Mediator for Client
            Mediator.Instance.Register(
                (Object o) =>
                {
                    Client client = (Client)o;
                    name = client.name;
                    active = client.active;
                    status = client.status;
                }, B2BNet.MVVM.ViewModelMessages.UpdateClientViews
            );
        }
   }
}
using System.ComponentModel;

namespace B2BNet.MVVM
{
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChangedEvent( string propertyName )
        {
            var handler = PropertyChanged;
            if ( handler != null )
                handler( this, new PropertyChangedEventArgs( propertyName ) );
        }
    }
}
using System;
using B2BNet.lib;

namespace B2BNet.MVVM
{
    /// <summary>
    /// Available cross ViewModel messages
    /// </summary>
    public enum ViewModelMessages { UpdateClientViews = 1,
                                    DebugUpdated = 2
                                   };


    public sealed class Mediator
    {
        #region Data
        static readonly Mediator instance = new Mediator();
        private volatile object locker = new object();

        MultiDictionary<ViewModelMessages, Action<Object>> internalList
            = new MultiDictionary<ViewModelMessages, Action<Object>>();
        #endregion

        #region Ctor
        //CTORs
        static Mediator()
        {


        }

        private Mediator()
        {

        }
        #endregion

        #region Public Properties

        /// <summary>
        /// The singleton instance
        /// </summary>
        public static Mediator Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Registers a callback to a specific message
        /// </summary>
        /// <param name="callback">The callback to use 
        /// when the message it seen</param>
        /// <param name="message">The message to 
        /// register to</param>
        public void Register(Action<Object> callback, 
            ViewModelMessages message)
        {
            internalList.AddValue(message, callback);
        }


        /// <summary>
        /// Notify all callbacks that are registed to the specific message
        /// </summary>
        /// <param name="message">The message for the notify by</param>
        /// <param name="args">The arguments for the message</param>
        public void Notify(ViewModelMessages message, 
            object args)
        {
            if (internalList.ContainsKey(message))
            {
                //forward the message to all listeners
                foreach (Action<object> callback in 
                    internalList[message])
                        callback(args);
            }
        }
        #endregion    
    }
}
using System;
using System.Windows.Input;

namespace B2BNet.MVVM
{
    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _action;

        public DelegateCommand( Action<object> action )
        {
            _action = action;
        }

        public void Execute( object parameter )
        {
            _action( parameter );
        }

        public bool CanExecute( object parameter )
        {
            return true;
        }

        #pragma warning disable 67
        public event EventHandler CanExecuteChanged;
        #pragma warning restore 67
    }
}
使用系统;
使用System.Windows.Input;
命名空间B2BNet.MVVM
{
公共类DelegateCommand:ICommand
{
私有只读操作\u操作;
公共委托人命令(操作)
{
_行动=行动;
}
public void Execute(对象参数)
{
_作用(参数);
}
公共布尔CanExecute(对象参数)
{
返回true;
}
#pragma警告禁用67
公共事件处理程序CanExecuteChanged;
#pragma警告恢复67
}
}
解决方案

解决办法很简单。谢谢你的快速回复,瑞秋:)。我简单地从每个视图中删除了以下内容:

<!--<Grid.DataContext>
        <VM:ClientPanel/>
    </Grid.DataContext>-->

<!--<Grid.DataContext>
        <VM:Client/>
    </Grid.DataContext>-->

它工作得非常完美,甚至允许我更新datagrid:


哦,天哪,这是一大堆代码,我不确定我是否理解你想做的每件事。以下是使用WPF绑定时的几个重要注意事项

  • 确保属性具有公共{get;set;}
  • 如果该值将要更改,请确保将Mode=TwoWay、UpdateSourceTrigger=PropertyChanged添加到绑定中。这将使视图和视图模型保持同步,并通知彼此进行更新
  • 对集合使用ObservableCollections和BindingList。清单似乎不太管用
  • 确保DataContext与您尝试绑定到的属性匹配。如果DataContext没有这些属性,它们就不会更新

  • 问题是您正在用户控件中硬编码DataContext


    因此,您的
    客户端
    用户控件将其DataContext硬编码为
    客户端
    的新实例,并且它与您的
    客户端面板
    用户控件使用的实例不同。因此,
    您的视图模型是私有的,我刚刚将类更新为public,但行为是相同的。我注意到的第一件事是,您的用户控制硬编码DataContext,这意味着不能向其中传递DataContext。这可能是你的问题。这解决了我的问题!非常感谢你!我确信我需要DataContext,因为我有它,所以我一直在错误的地方寻找答案。但我现在对它的工作原理有点困惑,因为我刚刚意识到,如果我所做的唯一更改是注释掉硬编码的DataContext,那么我就不需要添加DataContext=“{…}”在ClientPanel或在客户端…我相信你的其他帖子也回答了这个问题。我有一个此ClientPanel所在的父MainWindowView。在这一点上,我定义:@CorbinTarrant很高兴你把它解决了!DataContext是我经常看到WPF初学者对它感到困惑的东西,实际上我写了一篇关于它的博客文章,只是为了链接,如果你感兴趣的话——它可能比我在评论中能更好地解释事情:)