c# - WPF MVVM : Display View for DataGrid's SelectedItem

标签 c# wpf xaml mvvm datagrid

我是 MVVM 和 WPF 的新手,并且已经完全沉迷于这个问题有一段时间了。我正在尝试根据 DataGrid 中的 SelectedItem 显示 View (UserControl)。 UserControl 使用构造函数中的数据集进行呈现,但从不更新。

enter image description here

我真的很感谢有这方面经验的人提供一些见解。我尝试通过 setUpdateCallback 添加调解器,现在数据网格中的第一行使用我单击的其他行的值进行更新,但这显然不是我想要的,我需要这些更新发生在单独的客户端 View 中数据网格。

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>

Client.xaml

<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>

ViewModel - ClientPanel.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");
        }
    }
}

ViewModel - Client.cs

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
            );
        }
   }
}

非常基本的 MVVM 框架

ObservableObject.cs

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 ) );
        }
    }
}

Mediator.cs

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    
    }
}

DelegateCommand.cs

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
    }
}

解决方案

解决方案很简单。感谢雷切尔的快速回复:)。我简单地从每个 View 中删除了以下内容:

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

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

它工作得很好,甚至允许我更新数据网格:

enter image description here

最佳答案

问题是您正在对 UserControls 中的 DataContext 进行硬编码。

所以你的Client UserControl 将其 DataContext 硬编码为 Client 的新实例,并且它与您的 ClientPanel 不同。用户控件正在使用。所以<V:Client Content="{Binding currentClient}"绑定(bind)指向的属性实例与 DataGrid 使用的属性实例不同。

所以摆脱

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

并更改您的 Content绑定(bind)到DataContext一个

<V:Client DataContext="{Binding currentClient}" .../>

它应该可以正常工作。

我确信我对为什么你不应该硬编码DataContext有一些提示。 UserControl 的属性,但我现在能找到的最接近的东西是 this 。希望它可以帮助你:)

关于c# - WPF MVVM : Display View for DataGrid's SelectedItem,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33814890/

相关文章:

c# - .NET 计时器异步运行吗?

c# - 当从不同的窗口调用 repository.save 时,mvvm 更新 ListView

c# - 在代码隐藏 C# 中使用 DoubleAnimationUsingKeyFrames 创建 Storyboard

c# - Framework.Triggers 和 Style.Triggers 之间的区别?

c# - 所有获取数据库对象的 linq 都应该用 try catch 包围吗?

javascript - 无法从 asp :Label after changing it with javascript 获取文本属性

WPF - 访问不同命名空间中的静态类

wpf - 为数据网格行创建上下文菜单

c# - Jenkins 无法通过缺少对程序集 netstandard 的引用来构建 Xamarin iOS 项目

c# - DataGrid 自动生成的列 : Increasing width