【问题标题】:How to set ViewModel Properties from DataGrid Selected Row using Caliburn Micro如何使用 Caliburn Micro 从 DataGrid 选定行设置 ViewModel 属性
【发布时间】:2019-02-15 14:34:56
【问题描述】:

我正在使用 Caliburn Micro 开发一个 WPF (MVVM) 应用程序,我使用了属性中应驻留在 VM 中的标准措辞,在我的应用程序中,我有 3 个文本框和一个数据网格,我想要的是无论何时使用选择文本框必须显示所选值的任何行(仅限单选!),文本框将根据列显示不同的数据。我也为相同的结构建模,问题是我已经根据 CM 的命名约定将控件的 x:name 设置为属性,现在选择是从模型中获取的,所以如何设置本地 VM 属性来自 Model 属性。

我的模特:

public class PackageModel
{
    public int id { get; set; }
    public string sessionName { get; set; }
    public int sessionInMins { get; set; }
    public int sessionAmount { get; set; }
    public bool isActive { get; set; }
}

我的视图模型:

private string _packName;
    public string PackageName
    {
        get { return _packName; }
        set
        {
            _packName = value;
            NotifyOfPropertyChange(() => PackageName);
        }
    }

    private int _amount;
    public int Amount
    {
        get { return _amount; }
        set {
            _amount = value;
            NotifyOfPropertyChange(() => Amount);
        }
    }

    private int _mins;
    public int Mins
    {
        get { return _mins; }
        set {
            _mins = value;
            NotifyOfPropertyChange(() => Mins);
        }
    }

    private bool _isActive;
    public bool IsPackageActive
    {
        get { return _isActive; }
        set {
            _isActive = value;
            NotifyOfPropertyChange(() => IsPackageActive);
        }
    }

    private List<PackageModel> _packageList;
    public List<PackageModel> PackageList
    {
        get { return _packageList; }
        set
        {
            _packageList = value;
            NotifyOfPropertyChange(() => PackageList);
        }
    }

    private PackageModel _selectedPackage;
    public PackageModel SelectedPackage
    {
        get
        {
            return _selectedPackage;
        }
        set
        {
                _selectedPackage = value;
                NotifyOfPropertyChange(() => SelectedPackage);
        }
        }
    }

我的观点:

<!--ROW 1-->
    <TextBox 
        x:Name="PackageName"
        Width="210" 
        Margin="5 5"
        Controls:TextBoxHelper.Watermark="Package Name"
        Controls:TextBoxHelper.ClearTextButton="True"
        HorizontalAlignment="Left"
        Grid.Row="0" 
        Grid.Column="0"/>

    <!--ROW 2-->
    <StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal">
        <TextBox
            x:Name="Mins"
            Width="100" 
            Margin="5 5"
            Controls:TextBoxHelper.Watermark="Min(s)"
            Controls:TextBoxHelper.ClearTextButton="True"/>
        <TextBox Width="100"
                 x:Name="Amount"
                 Margin="5 5"
                 Controls:TextBoxHelper.Watermark="Amount"
                 Controls:TextBoxHelper.ClearTextButton="True"/>
        <CheckBox 
            x:Name="IsPackageActive" 
            Margin="5 5" 
            Content="Is Active?" 
            IsChecked="{Binding SelectedPackage.isActive}"/>
    </StackPanel>

    <!--ROW 3-->
    <DataGrid x:Name="PackageList" 
              SelectedItem="{Binding SelectedPackage, Mode=TwoWay}"
              Grid.Row="2"
              Grid.ColumnSpan="1"
              AutoGenerateColumns="False"
              CanUserAddRows="False"
              CanUserDeleteRows="False"
              CanUserSortColumns="False"
              CanUserReorderColumns="False"
              CanUserResizeRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding id}" Visibility="Hidden" IsReadOnly="True"/>
            <DataGridTextColumn Header="Name" Binding="{Binding sessionName}" Width="120" IsReadOnly="True"/>
            <DataGridTextColumn Header="Min(s)" Binding="{Binding sessionInMins}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Amount" Binding="{Binding sessionAmount}" IsReadOnly="True"/>
            <DataGridCheckBoxColumn IsReadOnly="True" Header="Is Active?" Binding="{Binding isActive}" Width="*"/>
        </DataGrid.Columns>
    </DataGrid>

如果我遵循任何错误的标准或设置属性的错误方式,请提出建议。

【问题讨论】:

  • 能否请您也显示您的 xaml 代码?也许你必须设置 DataContext。
  • 您是否从后面的代码中绑定了您的项目源?在 Xaml 中缺少将 itemsource 绑定到列表的部分。还将 INotifyPropertyChanged 实现为 Model 并使 TwoWay 绑定或 OneWay 取决于您的需要。如果您需要完整的答案,请告知
  • @NhanPhan DataContext 由 CaliburnMicro 自动设置,命名约定。
  • @Kaspar :我考虑过将 INotifyPropertyChanged 实现为模型,但我看到很多人有自己的方式,有人说 VM 应该有其他人说模型应该有的接口,我不知道是哪个方法是正确的,如果我在 Model 中实现它,我猜我们不需要那些 VM 本地属性。如果你能分享一个对我来说很有意义的例子,比如 newbee
  • 我可以做这个例子,但是没有 CaliburnMicro(纯 WPF + MVVM),可以吗?我对微约定不是很熟悉。根据 VM 或 M 是否应该实现 INotifyPropertyChanged,这取决于。 stack-discussion

标签: c# wpf mvvm data-binding caliburn.micro


【解决方案1】:

这些不是Calibro,但你说没关系。

我删除了属性 sessionInMins 和 sessionAmout 以缩短答案,您可以像其他属性一样添加它。

首先我将 INotifyPropertyChanged 添加到模型中,我还为属性(大写字母)制作了 PascalCase。 PascalCase 不是必需的,但它是最常见的约定。

public class PackageModel : INotifyPropertyChanged
    {
        private int _Id;
        public int Id
        {
            get { return _Id; }
            set
            {
                if (value != _Id)
                {
                    _Id = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private string _SessionName;
        public string SessionName
        {
            get { return _SessionName; }
            set
            {
                if (value != _SessionName)
                {
                    _SessionName = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private bool _IsActive;
        public bool IsActive
        {
            get { return _IsActive; }
            set
            {
                if (value != _IsActive)
                {
                    _IsActive = value;
                    NotifyPropertyChanged();
                }
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

我修改 VM 仅具有包列表和 SelectedPackage,我从名称包中删除列表并使其成为多个插件。还要制作一些默认构造函数来填充开发人员列表。正如您还建议的那样,现在不需要附加属性,稍后只需使用 SelectedPackage 。

    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            Packages = new List<PackageModel>()
            {
                new PackageModel()
                {
                    Id = 1, SessionName="Session1", IsActive = true
                },
                new PackageModel()
                {
                    Id = 2, SessionName="Session2", IsActive = false
                }
            };
        }

        private PackageModel _SelectedPackage;
        public PackageModel SelectedPackage
        {
            get { return _SelectedPackage; }
            set
            {
                if (value != _SelectedPackage)
                {
                    _SelectedPackage = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private List<PackageModel> _Packages;
        public List<PackageModel> Packages
        {
            get { return _Packages; }
            set
            {
                if (value != _Packages)
                {
                    _Packages = value;
                    NotifyPropertyChanged();
                }
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

并添加视图。进行手动绑定。当您更改网格中的项目时,文本框和复选框将自动更新,如果您更改文本框或复选框中的值,它也会更新 DataGrid。

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>
    <TextBox Width="210" Margin="5 5" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" Text="{Binding SelectedPackage.SessionName, Mode=TwoWay}"/>
    <StackPanel Grid.Row="1">
        <CheckBox Margin="5 5" Content="Is Active?" IsChecked="{Binding SelectedPackage.IsActive, Mode=TwoWay}"/>

    </StackPanel>
    <DataGrid ItemsSource="{Binding Packages,Mode=TwoWay,NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged }" 
          SelectedItem="{Binding SelectedPackage, Mode=TwoWay}"
          Grid.Row="2"
          AutoGenerateColumns="False"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          CanUserSortColumns="False"
          CanUserReorderColumns="False"
          CanUserResizeRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding Id}" Visibility="Hidden" IsReadOnly="True"/>
            <DataGridTextColumn Header="Name" Binding="{Binding SessionName}" Width="120" IsReadOnly="True"/>
            <DataGridCheckBoxColumn IsReadOnly="True" Header="Is Active?" Binding="{Binding IsActive}" Width="*"/>
        </DataGrid.Columns>
    </DataGrid>

</Grid>

如果解决方案适合您,请告诉我!

【讨论】:

    【解决方案2】:

    您可以将Text 属性显式绑定到适当的源属性(并从视图模型中删除Amount 属性),例如:

    <TextBox Width="100" x:Name="Amount" Margin="5 5" Text="{Binding SelectedPackage.sessionAmount}" />
    

    或者您可以在视图模型中设置相应的源属性:

    private PackageModel _selectedPackage;
    public PackageModel SelectedPackage
    {
        get
        {
            return _selectedPackage;
        }
        set
        {
            _selectedPackage = value;
            NotifyOfPropertyChange(() => SelectedPackage);
            if (_selectedPackage != null)
            {
                Amount = _selectedPackage.sessionAmount;
                //...
            }
            else
            {
                Amount = default(int);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-29
      • 2020-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-30
      • 2013-06-26
      相关资源
      最近更新 更多