如何从ViewModel访问View控件?

时间:2015-05-10 23:18:39

标签: c# design-patterns mvvm view viewmodel

我遇到过这种情况,我需要从View访问ViewModel中的控件。为了编码将所选项目从ComboBox添加到列表中的方法。

我的问题是如何从View访问ViewModel控件?我应该遵循一定的设计模式来实现这个目标吗?

下面的方法是在后面的View's代码中编码的,我知道如果遵循MVVM模式,由于涉及紧密耦合,这是不好的做法。这就是为什么我的目标是将此方法移到ViewModel

该方法的目的是从两个ComboBoxes中获取当前选定的项目并添加到Key/Value List

public void AddGradeSubjectChoiceToList()
{
    string SelectedSubjectName = "null data";
    int SelectedPoints = 01;


    SelectedSubjectName = subjectCmbBx.SelectedItem.ToString();

    try {

    SelectedPoints = int.Parse(ordinaryGradeCmbBx.SelectedValue.ToString());

    }
    catch (Exception e)
    {
        //log error here..

    }

    List<StringKeyValue> SubjectPointKVTemp = new List<StringKeyValue>();

    //Add selected pair to list
    SubjectPointKVTemp.Add(new StringKeyValue { Key = SelectedSubjectName, Value = SelectedPoints });

    SubjectPointKV = SubjectPointKVTemp;

}

MainPage的XAML设置如下,有两个组合框,用于主题和成绩。以及addGrade按钮将调用方法将所选对添加到列表中:

<Page x:Class="LC_Points.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
      xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
      xmlns:converter="using:LC_Points.Converter"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
      xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
      xmlns:local="using:LC_Points"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
      DataContext="{Binding Source={StaticResource Locator}}"
      mc:Ignorable="d">

    <Page.Resources>
        <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        <converter:BoolToNonVisibilityConverter x:Key="BoolToNonVisibilityConverter" />
    </Page.Resources>


    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40*" />
            <RowDefinition Height="20*" />
            <RowDefinition Height="30*" />
            <RowDefinition Height="30*" />
            <RowDefinition Height="20*" />
            <RowDefinition Height="20*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="4*" />
            <ColumnDefinition Width="3*" />
        </Grid.ColumnDefinitions>


        <ComboBox x:Name="subjectCmbBx"
                  Grid.Row="1"
                  Grid.ColumnSpan="2"
                  Width="174"
                  HorizontalAlignment="Left"
                  VerticalAlignment="Top"
                  DisplayMemberPath="Subject"
                  Header="Subjects"
                  ItemsSource="{Binding Subjects}"
                  PlaceholderText="Pick a subject" />


        <ComboBox x:Name="ordinaryGradeCmbBx"
                  Grid.Row="1"
                  Grid.Column="0"
                  Grid.ColumnSpan="2"
                  Width="170"
                  HorizontalAlignment="Right"
                  DisplayMemberPath="Key"
                  Header="Grades"
                  ItemsSource="{Binding OrdinaryGradePointKV}"
                  PlaceholderText="Pick a grade"
                  Visibility="{Binding IsHigherToggled,
                                       Mode=TwoWay,
                                       Converter={StaticResource BoolToNonVisibilityConverter}}" />


        <Button x:Name="addGradeBtn"
                Grid.Row="2"
                HorizontalAlignment="Left"
                Command="{Binding Path=AddGradeCommand}"
                Content="Add Grade" />


        <ToggleButton x:Name="ordinaryTglBtn"
                      Grid.Row="3"
                      Grid.ColumnSpan="2"
                      HorizontalAlignment="Center"
                      Content="Ordinary"
                      IsChecked="{Binding IsOrdinaryToggled,
                                          Mode=TwoWay}" />


    </Grid>


</Page>

ViewModel的精简实现如下以供参考,在评论中显示我如何平移实施AddGradeSubjectChoiceToList()方法:

namespace LC_Points.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        private ScoreModel _scoreModel;

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel(ScoreModel GradeModel)
        {

            _scoreModel = GradeModel;

            //call methods to initilise list data
            GetSubjectTypes();
            GetOrdinaryGradePairs();
        }

        public List<ScoreModel> Subjects { get; set; }
        public List<StringKeyValue> OrdinaryGradePointKV { get; set; }

        //ordinary toggle button bool
        private bool _isOrdinaryToggled;
        public bool IsOrdinaryToggled
        {
            get
            {
                return _isOrdinaryToggled;
            }
            set
            {
                _isOrdinaryToggled = value;
                RaisePropertyChanged("IsOrdinaryToggled");
            }
        }

        //Need to add same method from code behind to VM here
        //but don't have access to the View's controlsMethod to store Subject and Grade from Combo Boxes
        public void AddGradeSubjectChoiceToList()
        {

        }

        //This Relay Command is tied to the button in the View, that will be used
        //to call the AddGradeSubjectChoiceToList method
        RelayCommand addGradeCommand;
        public RelayCommand AddGradeCommand
        {
            get
            {
                if (addGradeCommand == null)
                {
                    addGradeCommand = new RelayCommand(() =>
                    {
                       AddGradeSubjectChoiceToList
                    });
                }
                return addGradeCommand;
            }
        }

        public class StringKeyValue
        {
            public string Key { get; set; }
            public int Value { get; set; }
        }

        public void GetOrdinaryGradePairs()
        {
            List<StringKeyValue> ordinaryGradePointKVTemp = new List<StringKeyValue>();


            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "A1", Value = 60 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "A2", Value = 50 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "B1", Value = 45 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "B2", Value = 40 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "B3", Value = 35 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "C1", Value = 30 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "C2", Value = 25 });
            ordinaryGradePointKVTemp.Add(new StringKeyValue { Key = "C3", Value = 20 });


            OrdinaryGradePointKV = ordinaryGradePointKVTemp;

        }

        public void GetSubjectTypes()
        {
            List<ScoreModel> subjectList = new List<ScoreModel>();

            // Adding Subjects to List
            subjectList.Add(new ScoreModel { Subject = "Accounting" });
            subjectList.Add(new ScoreModel { Subject = "Agricultural Economics" });
            subjectList.Add(new ScoreModel { Subject = "Agricultural Science" });
            subjectList.Add(new ScoreModel { Subject = "Ancient Greek" });
            subjectList.Add(new ScoreModel { Subject = "Applied Math" });
            subjectList.Add(new ScoreModel { Subject = "Arabic" });
            subjectList.Add(new ScoreModel { Subject = "Art" });
            subjectList.Add(new ScoreModel { Subject = "Artistic & Creative Group" });
            subjectList.Add(new ScoreModel { Subject = "Biology" });
            subjectList.Add(new ScoreModel { Subject = "Business" });

            Subjects = subjectList;
        }
    }
}

2 个答案:

答案 0 :(得分:0)

正如上面的评论指出的那样,在遵循MVVM模式时,将View与ViewModel耦合的解决方案是不好的做法。

适当的解决方案是为ComboBox的SelectedItem设置一个属性,并将View绑定到该属性,以便所选项可用于在VM中利用。

1。查看中设置与 SelectedItem 的绑定:

SelectedItem="{Binding SelectedSubject,Mode=TwoWay}"

2. ViewModel 中创建ComboBox属性:

        private ComboBox _selectedSubject;
        public ComboBox SelectedSubject
        {
            get { return _selectedSubject; }

            set
            {
                _selectedSubject = value;
                RaisePropertyChanged("SelectedSubject");
            }
        }

答案 1 :(得分:0)

为了遵循MVVM,您应该使用泛型类型并将它们绑定到控件。

以下是我的一个ViewModel中的示例:

private string[] _optionItems;
public string[] OptionItems
{
    get
    {
        return _optionItems;
    }
    set
    {
        if (_optionItems == value)
            return;

        _optionItems = value;
        OnPropertyChanged();
    }
}

private string _selectedOption;
public string SelectedOption
{
    get
    {
        return _selectedOption;
    }
    set
    {
        if (_selectedOption == value)
            return;

        _selectedOption = value;
        OnPropertyChanged();
    }
}

这是XAML代码:

<ComboBox ItemsSource="{Binding OptionItems}" SelectedItem="{Binding SelectedOption}"/>