【问题标题】:MVVM ViewModel creating and bindingMVVM ViewModel 创建和绑定
【发布时间】:2013-07-07 15:13:21
【问题描述】:

我打算用 Windows 窗体创建它,但被告知 wpf mvvm 会更好。我是 c# 新手,一直在研究 mvvm 和 wpf。

我现在正在处理我的视图模型,以便同时使用视图和模型。没有数据库。


我的问题:

如何正确地将视图绑定到视图模型。我在我的 xaml 中的某处缺少 itemssource 或 localsource 代码,但我也不明白 itemsource 是如何工作的。在视图模型中声明的 itemsource 以及如何声明的位置。我一直在谷歌上寻找一个好的答案,但仍然没有找到一个能让我点击的答案。

我也知道有一个 INotifyChange 类型的属性,我看过一些代码示例但不完全理解它,它只是没有为我点击。


目前:

我在 xaml 中创建了一个视图,这是下面的第一个代码。然后我创建了一个用于扫描的类,它是 C# 中的第二组代码(我知道 get set 方法可以改进,但我正在学习教程)。

使用扫描枪的用户在扫描时不会看着屏幕。我希望能够按顺序进行,因此第一次扫描填充第一个文本框,第二次扫描填充第二个文本框,如果需要,他们将填写计数。


额外信息:

底部(数据视图)是一个临时表,用于显示以前的扫描,但我可以认为这是我们以后的。最重要的部分是能够获得扫描结果并对其进行处理。

扫描将是keyboardwedge(发送字符,例如在最后输入带有回车键的字符)但稍后我计划将它们制作为串行com端口,以便该程序可以在后台运行。

注意:我知道我提供了很多对于当前的小问题可能不需要的细节,但只是想清楚一点。

<Window x:Class="ScanningV2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="700">
    <DockPanel LastChildFill="True">
        <Grid x:Name="LayoutRoot" DockPanel.Dock="Top" Height="100" Background="#FFFFFF" Margin="2,2,2,2">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Button Grid.Row="0" Grid.Column="0" Content="Scan" Grid.ColumnSpan="1" Margin="2,2,2.2,2" />
            <Label Content="Operator Barcode" Grid.Column="1" HorizontalAlignment="Left" Margin="50,20,0,0" VerticalAlignment="Top" Width="120" />
            <Label Content="MO/Task Barcode" Grid.Column="1" HorizontalAlignment="Left" Margin="200,20,0,0" VerticalAlignment="Top" Width="120" />
            <Label Content="Quantity" Grid.Column="1" HorizontalAlignment="Left" Margin="350,20,0,0" VerticalAlignment="Top" Width="120" />
            <TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="50,50,0,0" TextWrapping="Wrap" Text="Scan" VerticalAlignment="Top" Height="20" Width="120" />
            <TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="200,50,0,0" TextWrapping="Wrap" Text="Scan" VerticalAlignment="Top" Height="20" Width="120" />
            <TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="350,50,0,0" TextWrapping="Wrap" Text="Scan" VerticalAlignment="Top" Height="20" Width="120" />

            <!--            <ListView Grid.Row="0" Grid.Column="1" x:Name="curScans" Background="Aqua" Grid.ColumnSpan="1" Margin="1.8,0,-0.4,0">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Scanner" DisplayMemberBinding="{Binding Path=curScanNum}" Width="150" />
                        <GridViewColumn Header="Operator" DisplayMemberBinding="{Binding Path=curOperator}" Width="200" />
                        <GridViewColumn Header="Task" DisplayMemberBinding="{Binding Path=curTask}" Width="200"/>
                    </GridView>
                </ListView.View>
            </ListView> -->
        </Grid>
        <ListView x:Name="pastScans" Background="#2FFFFFFF" DockPanel.Dock="Bottom">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Scanner" DisplayMemberBinding="{Binding Path=ScannerNum}" Width="100" />
                    <GridViewColumn Header="Operator barcode" DisplayMemberBinding="{Binding Path=Operator}" Width="150" />
                    <GridViewColumn Header="MO/Task barcode" DisplayMemberBinding="{Binding Path=Task}" Width="150" />
                    <GridViewColumn Header="Date" DisplayMemberBinding="{Binding Path=ScanDate}" Width="100" />
                    <GridViewColumn Header="Time" DisplayMemberBinding="{Binding Path=ScanTime}" Width="100" />
                    <GridViewColumn Header="Quantity" DisplayMemberBinding="{Binding Path=Quantity}" Width="100" />
                </GridView>
            </ListView.View>
        </ListView>

    </DockPanel>

</Window>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ScanningV2
{
    class scan
    {
        //Member variables
        private string operatorCode;
        public string OperatorCode
        {
            get { return operatorCode; }
            set { operatorCode = value; }
        }

        private string taskCode;
        public string TaskCode
        {
            get { return taskCode; }
            set { taskCode = value; }
        }

        private int count;
        public int Count
        {
            get { return count; }
            set { count = value; }
        }

        private DateTime scanDateTime;
        public DateTime ScanDateTime
        {
            get { return scanDateTime; }
            set { scanDateTime = value; }
        }

        //Default Constructor
        public scan()
        {
            operatorCode = null;
            taskCode = null;
            count = 0;
        }

        //Overload Constructor
        public scan(string OperCode, string TaskMOCode, int CountNum)
        {
            operatorCode = OperCode;
            taskCode = TaskMOCode;
            count = CountNum;
        }
    }
}

【问题讨论】:

  • 您的扫描类需要继承自 INotifyPropertyChanged,并且每个实例都应该在一个可观察的集合中。它是应在您的 VM 中创建并绑定到 ListView 控件的项目源属性的可观察集合。获取生成 sn-p 的 c# 属性将在重构“扫描”类方面挽救您的生命!

标签: c# wpf mvvm


【解决方案1】:

您必须将视图模型类的实例设置为视图的 DataContext。我通常在视图的代码隐藏中执行此操作,因此在您的 MainWindow.xaml.cs 中您将执行以下操作:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new Scan();
    }
}

请记住,除非您通知它,否则您的视图将无法检测到更改。这就是 INotifyPropertyChanged 接口的重点:

class Scan : INotifyPropertyChanged
{
    // Implementing the INotifyPropertyChanged interface:
    public event PropertyChangedEventHandler PropertyChanged;

    // A utility method to make raising the above event a little easier:
    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    // Then, notify the view about changes whenever a property is set:
    private string operatorCode;
    public string OperatorCode
    {
        get { return operatorCode; }
        set { operatorCode = value; RaisePropertyChanged("OperatorCode"); }
    }
}

在您的 MainWindow.xaml 中,您可以绑定到该属性:

<TextBlock Text="{Binding OperatorCode}" />

现在,每当您为 OperatorCode 设置新值时,您的视图都会收到通知,以便它可以获取并显示新值。

对于 ItemsSources,任何 IEnumerable 都可以 - 一个列表、一个数组……但是,如果您希望在集合更改时通知视图,则必须使用实现 INotifyCollectionChanged 的​​类,例如 ObservableCollection。

因此,您在视图模型中创建了一个可绑定属性:

private ObservableCollection<string> names;
public ObservableCollection<string> Names
{
    get { return names; }
    set { names = value; RaisePropertyChanged("Names"); }
}

然后你从你的视图中绑定到它:

<ListView ItemsSource="{Binding Names}" />

小点:在 C# 中,类名通常用 CamelCase 编写。此外,我个人更喜欢给每个视图模型类一个 ViewModel 后缀,这样您就可以快速查看哪些类是视图模型。我尝试将他们的名称与他们所属的视图的名称相匹配,因此我将其称为“MainWindowViewModel”,而不是“扫描”。

【讨论】:

  • Pieter,谢谢,这是一个很好的出发点。你的很多小cmets把我在其他大部分阅读中缺少的东西放在一起。
【解决方案2】:

您无法将其中的任何内容绑定到任何 WPF UI 元素,因为您的代码太像 java。

您需要使用Properties C# 方式。

将您所有的 get()set() 方法更改为真实属性。

【讨论】:

  • 感谢 HighCore 的开始。我相信我已经正确地改变了它们。
猜你喜欢
  • 2013-06-14
  • 1970-01-01
  • 1970-01-01
  • 2011-01-31
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 2012-06-17
  • 2011-08-26
相关资源
最近更新 更多