0

我正在尝试创建一个示例纯 MVVM 应用程序。我的问题是,如果我将 Model 属性绑定到 UI 上的 ListView 项,它运行良好,但是当我尝试绑定 Model 属性的包装器 [在 ViewModel 中创建] 它不起作用。

在我的示例应用程序中,如果我在 FamilyView.xaml\ListView 控件中使用名称和位置 [模型中公开的属性] 属性,它会显示项目,但如果我使用 MemberName 和 MemberLocation [ViewModel 中公开的属性],它不会更新列表。

我对 MVVM 中层之间关系的理解是 ViewModel 将 View & Model 分开。如果是这样,那么我们应该使用 ViewModel 属性绑定到 View 而不是 Model 属性。请建议如何通过将其绑定到 ViewModel 属性来更新我的列表。

我的代码如下:

FamilyView.xaml

<Window x:Class="MVVM_15thSep13.View.FamilyView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVM_15thSep13.ViewModel"
        Title="FamilyView" Height="283" Width="367">
    <Window.DataContext>
        <local:FamilyViewModel/>
    </Window.DataContext>
    <Grid>
        <TextBox Text="{Binding Family.Name, FallbackValue=BindingFailed}" Height="23" HorizontalAlignment="Left" Margin="12,16,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
        <TextBox Text="{Binding Family.Location, FallbackValue=BindingFailed}"  Height="23" HorizontalAlignment="Left" Margin="12,57,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
        <Button Command="{Binding AddDetailsCommand}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="240,31,0,0" Name="button1" VerticalAlignment="Top" Width="93" />
        <ListView ItemsSource="{Binding FamilyCollection}" SelectedItem="{Binding Family}" Height="126" HorizontalAlignment="Left" Margin="14,110,0,0" Name="listView1" VerticalAlignment="Top" Width="319" UseLayoutRounding="True">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="120"/>
                    <GridViewColumn Header="Location" DisplayMemberBinding="{Binding Location}" Width="120"/>                    
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

家庭模型.cs

namespace MVVM_15thSep13.Model
{
    public class FamilyModel:ObservableObject
    {
        private string m_Name;
        private string m_Location;

        public string Name
        {
            get { return m_Name; }
            set 
            {
                m_Name = value;
                if (m_Name != value)
                    OnPropertyChanged("Name");
            }
        }

        public string Location
        {
            get { return m_Location; }
            set
            {
                m_Location = value;
                if (m_Location != value)
                    OnPropertyChanged("Location");
            }
        }

        public FamilyModel()
        {
            m_Name = "Default Name";
            m_Location = "Default Location";
        }

        public FamilyModel(string name, string location)
        {
            m_Name = name;
            m_Location = location;
        }
    }
}

FamilyViewModel.cs

 namespace MVVM_15thSep13.ViewModel
{
    public class FamilyViewModel:ObservableObject
    {
        private FamilyModel m_Family;        
        private ObservableCollection<FamilyModel> m_FamilyCollection;
        private ICommand m_AddDetailsCommand;


        public FamilyViewModel()
        {           
            m_Family = new FamilyModel();
            m_FamilyCollection = new ObservableCollection<FamilyModel>();            
        }

        public FamilyModel Family
        {
            get { return m_Family; }
            set 
            {
                if (m_Family != value)
                {
                    m_Family = value;
                    OnPropertyChanged("Family");
                }
            }
        }
        public ObservableCollection<FamilyModel> FamilyCollection
        {
            get { return m_FamilyCollection; }
            set { m_FamilyCollection = value; }
        }       

        public ICommand AddDetailsCommand
        {
            get
            {
                if (m_AddDetailsCommand == null)
                    m_AddDetailsCommand = new RelayCommand(param => AddFamilyDetails(), null);

                return m_AddDetailsCommand;
            }
        }

        public void AddFamilyDetails()
        {
            FamilyCollection.Add(Family);
            Family = new FamilyModel();
        }
    }
}

其他助手类:

中继命令.cs

namespace MVVM_15thSep13.HelperClasses
{
    public class RelayCommand:ICommand
    {
        private readonly Action<object> m_Execute;
        private readonly Predicate<object> m_CanExecute;

        public RelayCommand(Action<object> exec) : this(exec, null) { }
        public RelayCommand(Action<object> exec, Predicate<object> canExec)
        {
            if (exec == null)
                throw new ArgumentNullException("exec");

            m_Execute = exec;
            m_CanExecute = canExec;
        }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            if (parameter == null)
                return true;
            else
                return m_CanExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (m_CanExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (m_CanExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

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

        #endregion
    }
}

ObservableObject.cs

namespace MVVM_15thSep13.HelperClasses
{
    public abstract class ObservableObject:INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
        #endregion
    }
}
4

1 回答 1

0

您谈论视图模型包装器属性,但您没有包装任何东西。您的视图模型只是将模型的属性加倍。你有两个选择;直接在模型类上实现INotifyPropertyChanged接口并在视图模型中使用它们的实例(就像现在一样),或者实际上在视图模型中“包装”模型的属性:

private FamilyModel data;

public string Name
{
    get { return data.Name; }
    set { data.Name = value; OnPropertyChanged("Name"); }
}

public string Location
{
    get { return data.Location; }
    set { data.Location = value; OnPropertyChanged("Location"); }
}

更新>>>

对不起,我以为你可以自己解决剩下的问题。这里的实例FamilyModel是一个私有字段。这绝不是 UI 中的数据绑定。相反,您绑定“包装”属性:

<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Location}" />

data您可以在构造函数中或在特定的 等上设置私有字段Button.Click

data = GetDataFromDatabaseOrWherever();

但是,由于您已INotifyPropertyChanged直接在模型类上实现了接口,因此您不需要这样做......我只是建议这样做,因为您在问题标题中提到了“包装器属性”。

相反,您可以只使用您的FamilyandFamilyCollection属性并摆脱MemberNameandMemberLocation属性,除非它们用于特定的东西。使用该Family属性,您可以像这样绑定到 UI:

<TextBox Text="{Binding Family.Name}" />
<TextBox Text="{Binding Family.Location}" />

关于您的视图模型,我的主要观点是您要么使用包装的属性,要么直接使用您的数据类型,但您似乎两者都有(除非MemberName并且MemberLocation与您的模型无关。

如果您仍然遇到问题,请确保您已将视图的DataContext属性正确设置为视图模型的实例。

更新 2 >>>

好的,我加载了您的代码并发现了您的问题。

为了将来参考,如果您只看一下您Output Window在 Visual Studio 中遇到的错误,就可以很容易地解决您自己的问题……它确实是 WPF 开发人员最好的朋友。那里有两个错误:

BindingExpression 路径错误:在“object”“FamilyModel”上找不到“MemberLocation”属性 BindingExpression 路径错误:在“object”“FamilyModel”上找不到“MemberName”属性

在您的 XAML 中搜索MemberLocationand MemberName,我可以看到您的问题。您正在尝试从您的属性中的项目绑定到这些属性FamilyCollection......但这是一个ObservableCollection类型FamilyModel,并且FamilyModel该类中没有任何MemberLocationMemberName属性。有你的错误。要修复它,只需将您的更改GridView为:

<GridView>
    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="120"/>
    <GridViewColumn Header="Location" DisplayMemberBinding="{Binding Location}" Width="120"/>
</GridView>

就个人而言,我认为您应该摆脱您的MemberLocationMemberName财产……它们只是令人困惑的事情。相反,您应该Family像这样使用您的财产:

<TextBox Text="{Binding Family.Name, FallbackValue=BindingFailed}" ... />
<TextBox Text="{Binding Family.Location, FallbackValue=BindingFailed}" ... />

然后你可以像这样简化你的 add 方法:

public void AddFamilyDetails()
{
    FamilyCollection.Add(Family);
    Family = new FamilyModel();
}

这也将使您能够做到这一点:

<ListView ItemsSource="{Binding FamilyCollection}" SelectedItem="{Binding Family}" ... />

...如果您还没有解决,它将在es中显示所选项目的NameLocation值。但当然,这取决于你。ListViewTextBox

于 2013-09-17T15:24:55.507 回答