我是C#的新手并创建了一个工作正常的简单应用程序,但我想使用模式MVVM学习C#。所以我试图将我的应用程序迁移到MVVM并且我感到困惑
1)当打开时,应用程序扫描文件夹并索引格式化的所有文件 " [编号] [姓名]" - 它工作正常!
2)我有一个只有一个textBox的窗口。用户键入一个数字,然后按ENTER键。在这一刻,我有一个CatalogViewModel
,它是一个集合File
,应该选择textBox中数字指定的文件并打开它。
问题1:在MVVM中,我无法将数据从我的视图Main
传递到我的ViewModel CatalogViewModel
(我不确定我是否可以做得正确)
问题2:我无法处理ENTER键并触发CatalogViewModel
我对MVVM有点困惑,无法继续。我知道这很简单。请问,如何解决这个问题(请详细说明,我是C#及其所有概念的初学者)
更新1 :
尝试了janonimus'问题1的解决方案但是数据绑定只是一种方式。
VM的值转到VIEW,但VIEW上的更改不会转到VM。
我以这种方式实施了INotifyPropertyChanged
using Prism.Mvvm;
...
public class CatalogViewModel: BindableBase
{
private string selectedValue = "100";
public string SelectedValue
{
get { return selectedValue; }
set { SetProperty(ref selectedValue, value); }
}
但是Databind变成了ON WAY XAML
<TextBox x:Name="tbSelectedValue" Text="{Binding SelectedValue, Mode=TwoWay}"
更新2
我找到问题解决方案1 。 janonimus提供的代码只能以单向方式工作,因为TextBox.Text的默认行为是在失去焦点时upadte,但在我的情况下它永远不会失去焦点see this post
以下代码解决了问题1:
Text="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
问题2 可以通过Pedro Silva的回答解决
if (e.Key == Key.Enter && tbSelectedValue.Text != String.Empty)
{
vm.OpenSelectedFile();
tbSelectedValue.Text = String.Empty;
}
但我想使用ICommand
以更加软化的方式实现它。
按照janonimus发送的建议,我创建了BaseCommand类Exactly like this但是当我调用函数OpenSelectedFile
private BaseCommand<CatalogViewModel> _selectFileCommand;
public ICommand SelectFileCommand
{
get
{
if (_selectFileCommand == null)
{
_selectFileCommand = new BaseCommand<CatalogViewModel>(OpenSelectedFile, true);
}
return _selectFileCommand;
}
}
public void OpenSelectedFile()
{
try
{
OpenFileByNumber(Int32.Parse(SelectedValue));
}
catch (Exception e)
{
MessageBox.Show("Número inválido: \'" + SelectedValue + "\"",
"ERRO", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
Main.xaml.cs
namespace SLMT.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class Main : Window
{
public Main()
{
InitializeComponent();
DataContext = new CatalogViewModel();
chosenNumber.Focus();
}
// Permite inserir somente números
private void ChosenNumber_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^ 0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
private void ChosenNumber_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && chosenNumber.Text != String.Empty)
{
//catalog.OpenFileByNumber(ConvertToInt(numeroEscolhido.Text));
//catalog.OpenSelectedFile(); // Will become someting like this
chosenNumber.Text = String.Empty;
}
}
private int ConvertToInt(string value)
{
try
{
var str = value.Replace(" ", String.Empty);
return Int32.Parse(str);
}
catch (Exception exc)
{
MessageBox.Show("O número: \"" + chosenNumber.Text + "\" é inválido", "ERRO", MessageBoxButton.OK, MessageBoxImage.Error);
chosenNumber.Text = String.Empty;
return 0;
}
}
/// <summary>
/// Controll what wil lhappen if some KEYS are pressed on APP
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void GMain_KeyUp(object sender, KeyEventArgs e)
{
switch (e.Key){
case Key.Escape:
Environment.Exit(0);
break;
case Key.F1:
//wListFiles = new ListFiles(catalog);
//wListFiles.ShowDialog();
//numeroEscolhido.Text = wListFiles.SelectFile();
//numeroEscolhido.SelectAll();
break;
}
}
}
}
ps:我从没有MVVM的版本导入的注释行
Main.xaml
<Window x:Name="wMain" x:Class="SLMT.Views.Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SLMT.Views"
mc:Ignorable="d"
Title="Ministério Tons" Height="364" Width="700" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Grid x:Name="gMain" KeyUp="GMain_KeyUp">
<Image x:Name="imgBackground" HorizontalAlignment="Left" Height="364" VerticalAlignment="Top" Width="700" Source="/SLMT;component/Resources/img/background2.jpg" Opacity="100"/>
<TextBox x:Name="chosenNumber" HorizontalAlignment="Center" Height="34" Margin="500,294,56,36" TextWrapping="Wrap" VerticalAlignment="Center" Width="144" BorderBrush="{x:Null}" Background="{x:Null}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" UndoLimit="50" ForceCursor="True" PreviewTextInput="ChosenNumber_PreviewTextInput" KeyUp="ChosenNumber_KeyUp" BorderThickness="0" FontSize="20" Opacity="0.6" FontWeight="Bold"/>
</Grid>
</Window>
CatalogViewModel.cs的相关部分
namespace SLMT.ViewModel
{
public class CatalogViewModel: ObservableCollection <File>
{
private int selectedNumber;
/// <summary>
/// Contain the selected number in the View
/// </summary>
public int SelectedNumber
{
get { return selectedNumber; }
set { selectedNumber = value; }
}
// REMOVED CODE TO SCAN AND INDEX THE FILES
public CatalogViewModel() : base()
{
ScanFiles();
ValidateAndAddFiles();
ShowAlerts();
}
public void OpenSelectedFile()
{
OpenFileByNumber(SelectedNumber);
}
/// <summary>
/// Get the file from catalog identified my the number
/// </summary>
/// <param name="number"></param>
/// <returns>File|null</returns>
private File GetFileByNumber(int number)
{
foreach (var file in this)
{
if (file.number == number){
return file;
}
}
return null;
}
private void OpenFileByNumber(int number)
{
var file = GetFileByNumber(number);
if (file == null)
{
MessageBox.Show("Nenhum arquivo encontrado com o número: \'" + number +"\"",
"ARQUIVO NÃO ENCONTRADO", MessageBoxButton.OK, MessageBoxImage.Warning);
} else
{
file.Open();
}
}
}
}
答案 0 :(得分:1)
尝试以下操作以访问您在构造函数中创建的视图模型。
CatalogViewModel vm = new CatalogViewModel();
public Main()
{
InitializeComponent();
DataContext = vm;
chosenNumber.Focus();
}
然后在你的密钥处理程序中,你可以这样做:
private void ChosenNumber_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && chosenNumber.Text != String.Empty)
{
//catalog.OpenFileByNumber(ConvertToInt(numeroEscolhido.Text));
//catalog.OpenSelectedFile(); // Will become someting like this
vm.OpenSelectedFile();
chosenNumber.Text = String.Empty;
}
}
更新:使用ICommand
使用您从here指向的BaseCommand类。我将以下代码添加到CatalogViewModel并使其正常工作。您将BaseCommand中的类型作为视图模型,但这应该是命令参数的类型(基于该帖子中的示例)。
private BaseCommand<object> _selectFileCommand;
public ICommand SelectFileCommand
{
get
{
if (_selectFileCommand == null)
{
_selectFileCommand = new BaseCommand<object>((commandParam) => OpenSelectedFile(commandParam),
(commandParam) => CanOpenSelectedFile(commandParam));
}
return _selectFileCommand;
}
}
public void OpenSelectedFile(object commandParam = null)
{
Debug.WriteLine("CatalogViewModel.OpenSelectedFile was called.");
Debug.WriteLine("SelectedValue = " + this.SelectedValue);
}
public bool CanOpenSelectedFile(object commandParam = null)
{
return true;
}
进入OpenSelectedFile方法后,您应该能够将其连接到您想要的功能。
答案 1 :(得分:1)
你需要在这里做一些事情。
对于问题1,您必须绑定将数据传递给ViewModel的View。 在这种情况下,TextBox.Text属性必须绑定到CatalogViewModel.SelectedNumber属性:
<TextBox x:Name="chosenNumber"
...
Text={Binding SelectedNumber} />
对于MVVM,CatalogViewModel类必须实现INotifyPropertyChanged接口。只需为您拥有的ObservableCollection创建一个属性。
对于问题2,您需要使用KeyBinding和ICommand来完成这项工作。 在视图中,它应该如下所示:
<TextBox x:Name="chosenNumber"
...
Text={Binding SelectedNumber}>
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding SelectFileCommand}" />
</TextBox.InputBindings>
</TextBox>
在ViewModel中,您需要一个ICommand属性:
public class CatalogViewModel: INotifyPropertyChanged
{
private BaseCommand _selectFileCommand;
public ICommand SelectFileCommand
{
get
{
if (_selectFileCommand == null)
{
_selectFileCommand = new BaseCommand(SelectFile, CanSelectFile);
}
return _selectFileCommand;
}
}
...
其中,SelectFile是一个将执行操作的函数,CanSelectFile是函数,告诉命令是否可以执行,BaseCommand是ICommand接口的一个实现。 您可以参考以下问题:WPF selectedItem on Menu or get commandparameter in viewmodel
更新:
使用此BaseCommand
实施,而不是BaseCommand<T>
:
class BaseCommand : ICommand
{
private readonly Action _executeMethod = null;
private readonly Func<bool> _canExecuteMethod = null;
public BaseCommand(Action executeMethod, Func<bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute()
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod();
}
return true;
}
public void Execute()
{
if (_executeMethod != null)
{
_executeMethod();
}
}
}
试一试,让我们知道会发生什么。