英文:
Preventing null value insertion through a RelayCommand in WPF MVVM?
问题
以下是您提供的代码的翻译部分:
在你的代码中,你试图使用CanExecuteAddTodoCommand()
方法来控制AddTodoCommand
的可执行性,确保只有当NewTodo
不为空时才能执行该命令。你尝试了两种不同的实现方法。
首先,你尝试了以下方法:
public bool CanExecuteAddTodoCommand()
{
if (NewTodo == string.Empty)
{
MessageBox.Show("请填写备注!");
return false;
}
else
{
return true;
}
}
这个方法几乎正确,但有一个问题。如果NewTodo
是null
,它会通过验证并显示消息框。你只想在按钮被点击时才显示提示框,而不是在输入框为空时就显示。此外,虽然它在按下“Enter”键时可以通过,但似乎无法通过文本框输入来触发按钮。
然后,你尝试了以下方法:
public bool CanExecuteAddTodoCommand()
{
if (string.IsNullOrEmpty(NewTodo))
{
MessageBox.Show("请填写备注!");
return false;
}
else
{
return true;
}
}
这个方法会在NewTodo
为空时显示消息框,但同样存在无法通过文本框输入触发按钮的问题,因为它会一直阻止按钮的执行。
对于你的问题,你可以尝试以下修改来解决它:
public bool CanExecuteAddTodoCommand()
{
// 只有当NewTodo不为空并且不为null时,才允许执行命令
return !string.IsNullOrWhiteSpace(NewTodo);
}
这个修改将允许在文本框中输入内容后才能触发按钮,并且只有当NewTodo
既不为空也不为null
时才允许执行命令。这应该符合你的期望行为。
英文:
Good day, I am new to C# and WPF. Currently, I am trying to create a Todo list, but I am having problems with the CanExecuteAddTodoCommand() for my RelayCommand. I used MVVMlight for this and Material design in XAML for the XAML.
This is the code I'm working with.
Model:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;
using System.Windows;
using ToDoV3.Model;
namespace ToDoV3.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
/// </para>
/// <para>
/// You can also use Blend to data bind with the tool's support.
/// </para>
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private ObservableCollection<TodoModel> _todoList;
public ObservableCollection<TodoModel> TodoList
{
get { return _todoList; }
set { Set(ref _todoList, value); }
}
public MainViewModel()
{
TodoList = new ObservableCollection<TodoModel>();
}
private string _newTodo;
public string NewTodo
{
get { return _newTodo; }
set { Set(ref _newTodo, value); }
//Button Press Trigger
private RelayCommand _addTodoCommand;
public RelayCommand AddTodoCommand
{
get
{
return _addTodoCommand
?? (_addTodoCommand = new RelayCommand(ExecuteAddTodoCommand, CanExecuteAddTodoCommand));
}
}
//Adds the todo on clicking the button or pressing enter
public void ExecuteAddTodoCommand()
{
TodoList.Add(new TodoModel { TaskTodo = NewTodo, IsDone = false });
//Clears the box after pressing plus or enter
if (NewTodo != string.Empty)
{
NewTodo = string.Empty;
}
}
public bool CanExecuteAddTodoCommand()
{
if (NewTodo == string.Empty)
{
MessageBox.Show("please enter a note!");
return false;
}
else
{
return true;
}
}
}
}
View
<Window x:Class="ToDoV3.MainWindow"
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:ToDoV3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
DataContext="{Binding Main, Source={StaticResource Locator}}"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
Background="{DynamicResource MaterialDesignPaper}"
TextElement.FontWeight="Medium"
TextElement.FontSize="14"
FontFamily="{materialDesign:MaterialDesignFont}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
<RowDefinition Height="85"/>
</Grid.RowDefinitions>
<!--Where tasks are displayed-->
<ScrollViewer>
<ItemsControl
Margin="12,0,12,0"
Grid.IsSharedSizeScope="True"
ItemsSource="{Binding TodoList}">
<ItemsControl.ItemTemplate>
<DataTemplate
>
<Border
x:Name="Border"
Padding="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
SharedSizeGroup="Checkerz" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox
VerticalAlignment="Center"
IsChecked="{Binding IsDone}" />
<StackPanel
Grid.Column="1"
Margin="8,0,0,0">
<TextBlock
FontWeight="Bold"
Text="{Binding TaskTodo}" />
</StackPanel>
</Grid>
</Border>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding IsSelected}"
Value="True">
<Setter
TargetName="Border"
Property="Background"
Value="{DynamicResource MaterialDesignSelection}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!--This holds the status count-->
<Grid Grid.Row="1" Background="#212121" >
<TextBlock Foreground="#FFF"
Margin="10 7 0 0"
FontSize="12"
Text="{Binding CountStatus}"/>
</Grid>
<!--This holds the textbox and button-->
<Grid Grid.Row="2" Background="#212121">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBox
Grid.Column="0"
Margin="10 0 10 0"
Background="#FFF"
Height="70"
VerticalAlignment="Center"
materialDesign:HintAssist.Hint="Type your todo here"
IsEnabled="{Binding Path=IsChecked, ElementName=MaterialDesignOutlinedTextBoxEnabledComboBox}"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" >
<TextBox.Text>
<Binding Path="NewTodo" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
<TextBox.InputBindings>
<KeyBinding Command="{Binding AddTodoCommand}" Key="Enter"/>
</TextBox.InputBindings>
</TextBox>
<Button
Command="{Binding AddTodoCommand}"
Grid.Column="1"
IsEnabled="{Binding DataContext.ControlsEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
Style="{StaticResource MaterialDesignFloatingActionLightButton}"
ToolTip="MaterialDesignFloatingActionLightButton">
<materialDesign:PackIcon Kind="Plus" Width="24" Height="24"/>
</Button>
</Grid>
</Grid>
</Window>
This is where I specifically have a problem with. This is the first method I tried.
public bool CanExecuteAddTodoCommand()
{
if (NewTodo == string.Empty)
{
MessageBox.Show("please enter a note!");
return false;
}
else
{
return true;
}
}
The output I expect is that whenever the add button is pressed it would only work if NewTodo is not empty. It works but it would pass through a blank and would appear in the note list after that, it would not pass through input if the textbox were blank.
I've tried switching it to null since I think the reason it passes it's because it's null rather than empty. So, I changed it to something like this.
public bool CanExecuteAddTodoCommand()
{
if (string.IsNullOrEmpty(NewTodo))
{
MessageBox.Show("please enter a note!");
return false;
}
else
{
//return NewTodo != string.Empty || NewTodo == null;
return true;
}
}
It now sees that the textbox is null and sent the message box, but I only want this prompt to appear only when I triggered a push. The message box would immediately show, and the button cannot be pressed. Also, for some reason I can pass through what's inside the textbox to the observableobject and it would be displayed by pressing enter but as mentioned earlier, the code above does disable the button so I can't send anything through it. Lastly, there are parts of the code mentioning a checkbox but that's not related to this question.
Any thoughts on this would be highly appreciated.
TLDR:
Tried two ways of implementing the CanExecuteAddTodoCommand().
This one is almost correct albeit with one problem. It would pass a null since the if else statement only checked for empty not null.
The second CanExecuteAddTodoCommand() was changed to see if the NewTodo was null, the message would send by pressing enter but it would not allow me to send using the textbox.
答案1
得分: 0
如果要在TextBox
中没有文本时禁用命令,您应该实现CanExecuteAddTodoCommand
方法,类似于以下方式:
public bool CanExecuteAddTodoCommand() => !string.IsNullOrEmpty(NewTodo);
但如果您希望在"只有在触发推送时才出现提示",您应该始终启用命令,并在Execute
方法中显示MessageBox
:
public void ExecuteAddTodoCommand()
{
if (string.IsNullOrEmpty(NewTodo))
{
MessageBox.Show("请输入一个注释!");
return false;
}
TodoList.Add(new TodoModel { TaskTodo = NewTodo, IsDone = false });
// 在按加号或回车后清除文本框
if (NewTodo != string.Empty)
{
NewTodo = string.Empty;
}
}
public bool CanExecuteAddTodoCommand() => true;
无法控制框架何时调用CanExecute
方法,因此在此方法中显示MessageBox
是一个不好的主意。
还记得在NewTodo
属性的setter中刷新状态命令:
private string _newTodo;
public string NewTodo
{
get { return _newTodo; }
set
{
Set(ref _newTodo, value);
_addTodoCommand.RaiseCanExecuteChanged();
}
}
这会引发CanExecuteChanged
事件,这将导致框架调用CanExecute
方法以确定命令是否仍然应启用或禁用。
英文:
If you want to disable the command when there is no text in the TextBox
, you should implement the CanExecuteAddTodoCommand
method something like this:
public bool CanExecuteAddTodoCommand() => !string.IsNullOrEmpty(NewTodo);
But if you want a "prompt to appear only when you triggered a push", you should always enable the command and display the MessageBox
in the Execute
method:
public void ExecuteAddTodoCommand()
{
if (string.IsNullOrEmpty(NewTodo))
{
MessageBox.Show("please enter a note!");
return false;
}
TodoList.Add(new TodoModel { TaskTodo = NewTodo, IsDone = false });
//Clears the box after pressing plus or enter
if (NewTodo != string.Empty)
{
NewTodo = string.Empty;
}
}
public bool CanExecuteAddTodoCommand() => true;
You cannot control when the framework calls the CanExecute
method so displaying a MessageBox
in this method is a bad idea.
Also remember to refresh the status command in the setter of the NewTodo
property:
private string _newTodo;
public string NewTodo
{
get { return _newTodo; }
set
{
Set(ref _newTodo, value);
_addTodoCommand.RaiseCanExecuteChanged();
}
}
This raises the CanExecuteChanged
event which will cause the framework to call the CanExecute
method to determine whether the command should still be enabled or disabled.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论