如何从 ViewModel 访问视图控件?
How to access View control's from the ViewModel?
我遇到过这样的情况,我需要从 ViewModel
访问 View
中的控件。为了编写一种方法,将 ComboBox
中的所选项目添加到列表中。
我的问题是如何从 ViewModel
访问 View
控件?我应该遵循某种特定的设计模式来允许这样做吗?
下面的方法编码在后面的 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;
}
}
}
正如上面的评论所指出的,在遵循 MVVM 模式时,将 View 耦合到 ViewModel 的解决方案是不好的做法。
适当的解决方案是为 ComboBox 的 SelectedItem
设置一个 属性 并将视图绑定到它,以便可以在 VM 中利用所选项目。
1. 在 View 中设置绑定到 SelectedItem:
SelectedItem="{Binding SelectedSubject,Mode=TwoWay}"
2. 在 ViewModel:
中创建组合框 属性
private ComboBox _selectedSubject;
public ComboBox SelectedSubject
{
get { return _selectedSubject; }
set
{
_selectedSubject = value;
RaisePropertyChanged("SelectedSubject");
}
}
为了遵循 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}"/>
我遇到过这样的情况,我需要从 ViewModel
访问 View
中的控件。为了编写一种方法,将 ComboBox
中的所选项目添加到列表中。
我的问题是如何从 ViewModel
访问 View
控件?我应该遵循某种特定的设计模式来允许这样做吗?
下面的方法编码在后面的 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;
}
}
}
正如上面的评论所指出的,在遵循 MVVM 模式时,将 View 耦合到 ViewModel 的解决方案是不好的做法。
适当的解决方案是为 ComboBox 的 SelectedItem
设置一个 属性 并将视图绑定到它,以便可以在 VM 中利用所选项目。
1. 在 View 中设置绑定到 SelectedItem:
SelectedItem="{Binding SelectedSubject,Mode=TwoWay}"
2. 在 ViewModel:
中创建组合框 属性 private ComboBox _selectedSubject;
public ComboBox SelectedSubject
{
get { return _selectedSubject; }
set
{
_selectedSubject = value;
RaisePropertyChanged("SelectedSubject");
}
}
为了遵循 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}"/>