WPF / MVVM摘要常用方法

时间:2017-12-26 18:33:38

标签: c# wpf xaml mvvm

SO社区,请耐心等待我,因为我是新手,我还在学习(慢慢地)。我试图在网上完全实现所有可能的解决方案但没有成功。我想我完全把自己与DependencyMethods,DependencyProperties,RelayCommands,ICommand,BaseViewModels等混淆了........

这是我的场景......我有一个MainWindow(MainWindow),其中一个Frame包含一个UserControl(SampleUC)。 MainWindow和UserControl DataContexts指向它们各自的ViewModel(MainWindowVM,SampleUCVM)。 MainWindowVM和SampleUCVM是Base ViewModel(BASEVM)的子代,它通过ObservableCollection帮助器类使用INotifyPropertyChanged。 SampleUC中有一个Combobox,它使用方法“fillFacility”保存在SampleUCVM中构造的Facilities ObservableCollection,并在Combobox中使用“GetFacilityNum()”方法选择时存储SelectedFacilityNum。

我想从SampleUCVM中提取“fillFacility”和“GetFacilityNum”方法,并将它们放在BASEVM或单独的类中,以便其他ViewModel可以访问和使用它们。它们将在我的整个项目中反复使用。有人能够向我解释如何做到这一点?感谢您的帮助和耐心!

SampleUC.xaml

    <Grid>
    <Label Content="Facility" HorizontalAlignment="Left" Margin="10,32,0,0" VerticalAlignment="Top" Width="87" Height="27"/>
    <ComboBox Name="cboFacilities" 
              HorizontalAlignment="Left" Margin="119,37,0,0" VerticalAlignment="Top" Width="120"
              DisplayMemberPath="FacilityName"
              SelectedValuePath="FacilityName"
              ItemsSource="{Binding Facilities}"
              SelectedValue="{Binding SelectedFacility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
              />
</Grid>

SampleUCVM

public class SampleUCVM : BASEVM
{

    #region MySQL Connection

    const string dbConnectionString = @"datasource=localhost;port=3306;Initial Catalog='optest1a1';username=root;password=";

    #endregion

    private ObservableCollection<Facilities> _facilitiesList;
    private string _selectedFacility;
    private int _selectedFacilityNum;

    public ObservableCollection<Facilities> Facilities
    {
        get { return _facilitiesList; }
        set
        {
            SetProperty(ref _facilitiesList, value, () => Facilities);
        }
    }

    public string SelectedFacility
    {
        get { return _selectedFacility; }
        set
        {
            SetProperty(ref _selectedFacility, value, () => SelectedFacility);
            if (_selectedFacility != null)
            {
                GetFacilityNum();
            }
        }
    }

    public int SelectedFacilityNum
    {
        get { return _selectedFacilityNum; }
        set { SetProperty(ref _selectedFacilityNum, value, () => SelectedFacilityNum); }
    }

    public SampleUCVM()
    {
        Facilities = new ObservableCollection<Facilities>();
        fillFacilities();
    }

    private void fillFacilities()
    {
        using (MySqlConnection con = new MySqlConnection(dbConnectionString))
        {
            Facilities = new ObservableCollection<Facilities>();
            con.Open();
            string Query = "SELECT * FROM facilities";
            MySqlCommand createCommand = new MySqlCommand(Query, con);
            MySqlDataReader dr = createCommand.ExecuteReader();
            int count = 1;
            while (dr.Read())
            {
                string FacilityName = dr.GetString(1);
                Facilities facilityname = new Facilities(count, FacilityName);
                Facilities.Add(facilityname);
                count++;
            }
            con.Close();
        }
    }

    private void GetFacilityNum()
    {
        if (SelectedFacility != null)
        {
            using (MySqlConnection con = new MySqlConnection(dbConnectionString))
            {
                con.Open();
                string Query = "SELECT Facility_ID_Num FROM facilities WHERE Facility_Name='" + SelectedFacility + "' ";
                MySqlCommand createCommand = new MySqlCommand(Query, con);
                MySqlDataReader dr = createCommand.ExecuteReader();
                int count = 1;
                while (dr.Read())
                {
                    int FacilityNum = dr.GetInt32(0);
                    SelectedFacilityNum = FacilityNum;
                    count++;
                }
                con.Close();
            }
        }
    }
}

BASEVM

    public class BASEVM : ObservableObject
{
    public BASEVM()
    {

    }
}

3 个答案:

答案 0 :(得分:0)

你做的事情比实际情况复杂得多。 ObservableCollection<T>足以通知WPF控件到目前为止发生的变化;只需确保它与XAML绑定正确。

就这样

public ObservableCollection<Facilities> Facilities { get { return _facilitiesList; } set { SetProperty(ref _facilitiesList, value, () => Facilities); } }

是多余的,应该简化为

public ObservableCollection<Facilities> Facilities { get; }

并且只从构造函数初始化一次。

只要您希望它在基本视图模型中,只需将代码移动到那里即可。然后从您的基础模型继承您的子模型,因此孩子将获得这样的属性作为他们自己的一部分。

忘记SetValue(...),除非您注册了自己的自定义DependencyProperty,这仅适用于高级方案。

这些是起点。

玩得开心。

答案 1 :(得分:0)

我同意Peter Duniho你的问题太宽泛,而且没有明确的答案,但我想向你强调一些观点。 您BASEVM的目的是什么?为什么它继承自ObservableObject

  

包装对象的类,以便其他类可以通知   改变事件。 通常,此类设置为依赖项属性   在DependencyObjects上,并允许其他类观察任何更改   中的值

如果您想要基本的通知更改功能,您可以执行以下操作。

public class ViewModelBase:INotifyPropertyChanged{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        PropertyChanged?.Invoke(this, args);
    }
    protected void RaisePropertyChanged([CallerMemberName]string propertyName = null)
    {
        OnPropertyChanged(propertyName);
    }
    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value)) return false;

        storage = value;
        RaisePropertyChanged(propertyName);

        return true;
    }
}

source
之后,您继承此ViewModelBase类。当任何属性发生变化时,您可以像这样触发PropertyChanged事件:

public int SelectedFacilityNum
{
    get { return _selectedFacilityNum; }
    set { SetProperty(ref _selectedFacilityNum,value); }
}
  

我想提出方法&#34; fillFacility&#34;和   &#34; GetFacilityNum&#34;从SampleUCVM中将它们放入   BASEVM或可以访问和使用它们的单独类   其他ViewModels

fillFacility方法,如您的代码所示,正在处理database。据我所知,在MVVM模式中,ViewModels旨在冥想ViewModel

  

视图模型充当视图和模型之间的中介,   并负责处理视图逻辑。通常,视图   model通过调用模型中的方法与模型交互   类。然后,视图模型以表单形式提供模型中的数据   该视图可以轻松使用。视图模型从中检索数据   模型,然后使数据可用于视图,并可能重新格式化   以某种方式使数据更容易处理视图。

因此,我建议将Data Access逻辑从您的视图模型中分离到单独的Data Access图层。例如,您可以使用Repository模式并创建FacilityRepository。然后,您可以将此存储库提供给ViewModels需要此功能。如果你想要更严格,可以创建IFacilityRepository接口,让FacilityRepository实现它,然后将此接口注入任何需要此功能的ViewModel

答案 2 :(得分:0)

我会考虑为这种事情实现数据访问层。例如:

public class DataAccess
{
    public ObservableCollection<Facilities> GetFacilities()
    {
        ObservableCollection<Facilities> facilities = new ObservableCollection<Facilities>();

        using (MySqlConnection con = new MySqlConnection(dbConnectionString))
        {
            con.Open();
            string Query = "SELECT * FROM facilities";
            MySqlCommand createCommand = new MySqlCommand(Query, con);
            MySqlDataReader dr = createCommand.ExecuteReader();
            int count = 1;
            while (dr.Read())
            {
                string FacilityName = dr.GetString(1);
                facilities facilityname = new Facilities(count, FacilityName);
                facilities.Add(facilityname);
                count++;
            }
            con.Close();
        }

        return facilities;
    }
}

然后,您可以在ViewModel类(或某些ViewModel基类)上包含此实例。

关于您的GetFacilityNum()方法,这也可以进入DataAccess课程,但我个人认为更好的解决方案是将Facility_ID_Num作为您{的属性{1}}课程,如果可能的话。然后,您只需要修改Facilities方法以将其拉入,但随后在应用程序中,您始终可以在没有任何数据库调用的情况下访问它,因为它将成为您{{1}的一部分模特。