MvvmLight 中的 RelayCommand 类具有两个实现。
在 GalaSoft.MvvmLight.Command 命名空间和 GalaSoft.MvvmLight.CommandWpf 命名空间中。
您可能已经在命名空间 GalaSoft.MvvmLight.Command 中使用过。
而且这种类型实际上不会更新命令的状态。
如果在 GalaSoft.MvvmLight.CommandWpf 名称空间中使用,则命令的状态将根据预定的逻辑进行更新。
,
有没有可以在XAML中执行此操作的方法?有约束力吗?
只需将2020-08-04 T13:31:44.342Z
的{{1}}属性绑定到视图模型的class CreateData < ActiveRecord::Migration[6.0]
def change
create_table :data do |t|
t.float :temperature
t.float :windspeed
t.references :user,foreign_key: true
t.timestamps
end
end
end
源属性,然后从此setter引发命令的Text
方法一个。
如果您出于某些原因确实想处理实际事件,则应调查interaction triggers。
,
@Harald Coppulse,你绝对正确!
这是我对MvvmLight的测试代码。
ViewModel:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
namespace InvalidateCommandMvvmLight.ViewModel
{
public class MyViewModel : ViewModelBase
{
private string _text;
private int _number;
public string Text { get => _text; private set => Set(ref _text,value); }
public int Number { get => _number; set => Set(ref _number,value); }
public RelayCommand<string> CommandTest { get; }
public RelayCommand<int> CommandNumber { get; }
public MyViewModel()
{
CommandTest = new RelayCommand<string>(Test,CanTest);
CommandNumber = new RelayCommand<int>(IntTest,CanIntTest);
}
private bool CanTest(string text)
{
// the text must have a minimum length of 4
// and be different from the current one
return text != null && text.Length >= 4 && text != Text;
}
private void Test(string text)
{
Text = text;
}
private bool CanIntTest(int num)
{
// The "num" parameter must be positive,less than 100
// and is not equal to the Number property
return num > 0 && num <100 && num != Number;
}
private void IntTest(int num)
{
Number = num;
}
}
}
XAML:
<Window x:Class="InvalidateCommandMvvmLight.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:InvalidateCommandMvvmLight"
xmlns:vm="clr-namespace:InvalidateCommandMvvmLight.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MyViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox x:Name="tbText"
Text="Alle eendjes zwemmen in het water" VerticalAlignment="Center"
/>
<Button Content="Change Text"
Grid.Column="1"
Margin="5"
Padding="5,2"
Command="{Binding Path=CommandTest}"
CommandParameter="{Binding ElementName=tbText,Path=Text}"/>
<TextBox Text="{Binding Text,Mode=OneWay}" Grid.Column="2" IsReadOnly="True" VerticalAlignment="Center"/>
<TextBox x:Name="tbNumber"
Grid.Row="1"
Text="55" VerticalAlignment="Center"/>
<Button Content="Change Number"
Grid.Row="1" Grid.Column="1"
Margin="5"
Padding="5,2"
Command="{Binding Path=CommandNumber}"
CommandParameter="{Binding ElementName=tbNumber,Path=Text}"/>
<TextBox Text="{Binding Number,Mode=OneWay}" IsReadOnly="True"
Grid.Row="1" Grid.Column="2" VerticalAlignment="Center"/>
</Grid>
</Window>
不幸的是,MvvmLight中的CommandsWpf.RelayCommand类未正确实现。
它没有考虑到在WPF中使用不同类型的值的特殊性。
要以典型的WPF方式工作,实现应具有以下内容:
using System.ComponentModel;
namespace Common
{
#region Delegates for WPF Command Methods
/// <summary>Delegate of the executive team method.</summary>
/// <param name="parameter">Command parameter.</param>
public delegate void ExecuteHandler<T>(T parameter);
/// <summary>Command сan execute method delegate.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> if command execution is allowed.</returns>
public delegate bool CanExecuteHandler<T>(T parameter);
#endregion
/// <summary>Class for typed parameter commands.</summary>
public class RelayCommand<T> : RelayCommand
{
/// <summary>Command constructor.</summary>
/// <param name="execute">Executable command method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayCommand(ExecuteHandler<T> execute,CanExecuteHandler<T> canExecute = null)
: base
(
p => execute(TypeDescriptor.GetConverter(typeof(T)).IsValid(p) ? (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(p) : default),p => (canExecute == null) || (TypeDescriptor.GetConverter(typeof(T)).IsValid(p) && canExecute((T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(p)))
)
{}
}
}
除非您有能力更改RelayCommand实现,否则需要以某种方式使用Binding的功能来自动转换值。
一个变体。
在ViewModel中创建所需类型的属性,并将其用作自动转换的代理。
但是,如果输入了非数字值,则该命令将无法对其进行定义。
您还需要检查Validation.HasError。
ViewModel:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
namespace InvalidateCommandMvvmLight.ViewModel
{
public class MyViewModel : ViewModelBase
{
private string _text;
private int _number;
private int _numberView;
public string Text { get => _text; private set => Set(ref _text,value); }
public int NumberView { get => _numberView; set => Set(ref _numberView,less than 100
// and is not equal to the Number property
return num > 0 && num <100 && num != Number;
}
private void IntTest(int num)
{
Number = num;
}
}
}
XAML:
<Window x:Class="InvalidateCommandMvvmLight.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:InvalidateCommandMvvmLight"
xmlns:vm="clr-namespace:InvalidateCommandMvvmLight.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MyViewModel NumberView="55"/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox x:Name="tbText"
Text="Alle eendjes zwemmen in het water" VerticalAlignment="Center"
/>
<Button Content="Change Text"
Grid.Column="1"
Margin="5"
Padding="5,Mode=OneWay}" Grid.Column="2" IsReadOnly="True" VerticalAlignment="Center"/>
<TextBox x:Name="tbNumber"
Grid.Row="1"
Text="{Binding NumberView,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center"/>
<Button Content="Change Number"
Grid.Row="1" Grid.Column="1"
Margin="5"
Padding="5,2"
Command="{Binding Path=CommandNumber}"
CommandParameter="{Binding NumberView}">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(Validation.HasError),ElementName=tbNumber}"
Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<TextBox Text="{Binding Number,Mode=OneWay}" IsReadOnly="True"
Grid.Row="1" Grid.Column="2" VerticalAlignment="Center"/>
</Grid>
</Window>
第二个变种。
创建一个显式代理转换器。
转换器:
using System;
using System.ComponentModel;
using System.Windows;
namespace InvalidateCommandMvvmLight
{
public class ProxyBinding : Freezable
{
public Type Type
{
get { return (Type)GetValue(TypeProperty); }
set { SetValue(TypeProperty,value); }
}
// Using a DependencyProperty as the backing store for Type. This enables animation,styling,binding,etc...
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register(nameof(Type),typeof(Type),typeof(ProxyBinding),new PropertyMetadata(typeof(object),ChangedValueOrType));
private static void ChangedValueOrType(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
ProxyBinding proxy = (ProxyBinding)d;
if (proxy.Type == null)
{
proxy.Value = null;
return;
}
if (proxy.Source == null)
return;
if (proxy.Type == proxy.Source.GetType())
return;
if (TypeDescriptor.GetConverter(proxy.Type).IsValid(proxy.Source))
proxy.Value = TypeDescriptor.GetConverter(proxy.Type).ConvertFrom(proxy.Source);
else
proxy.Value = null;
}
public object Source
{
get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty,value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation,etc...
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register(nameof(Source),typeof(object),new PropertyMetadata(null,ChangedValueOrType));
public object Value
{
get { return GetValue(ValueProperty); }
protected set { SetValue(ValuePropertyKey,value); }
}
// Using a DependencyProperty as the backing store for readonly Value. This enables animation,etc...
protected static readonly DependencyPropertyKey ValuePropertyKey =
DependencyProperty.RegisterReadOnly(nameof(Value),new PropertyMetadata(null));
public static readonly DependencyProperty ValueProperty = ValuePropertyKey.DependencyProperty;
protected override Freezable CreateInstanceCore()
{
return new ProxyBinding();
}
}
}
XAML:
<Window x:Class="InvalidateCommandMvvmLight.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:InvalidateCommandMvvmLight"
xmlns:vm="clr-namespace:InvalidateCommandMvvmLight.ViewModel"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MyViewModel/>
</Window.DataContext>
<Window.Resources>
<local:ProxyBinding x:Key="ProxyInt"
Type="{x:Type sys:Int32}"
Source="{Binding ElementName=tbNumber,Path=Text,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox x:Name="tbText"
Text="Alle eendjes zwemmen in het water" VerticalAlignment="Center"
/>
<Button Content="Change Text"
Grid.Column="1"
Margin="5"
Padding="5,2"
Command="{Binding Path=CommandTest}"
CommandParameter="{Binding ElementName=tbText,Mode=OneWay}" Grid.Column="2" IsReadOnly="True" VerticalAlignment="Center"/>
<TextBox x:Name="tbNumber"
Grid.Row="1"
Text="55" VerticalAlignment="Center"/>
<Button Content="Change Number"
Grid.Row="1" Grid.Column="1"
Margin="5"
Padding="5,2"
Command="{Binding Path=CommandNumber}"
CommandParameter="{Binding Value,Source={StaticResource ProxyInt}}">
</Button>
<TextBox Text="{Binding Number,Mode=OneWay}" IsReadOnly="True"
Grid.Row="1" Grid.Column="2" VerticalAlignment="Center"/>
<TextBlock Grid.Row="2" Text="{Binding Value,Source={StaticResource proxy}}"/>
</Grid>
</Window>
另一个变种。
为绑定创建转换器:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Data;
namespace InvalidateCommandMvvmLight
{
public class ValueTypeConverter : IValueConverter
{
public object Convert(object value,Type targetType,object parameter,CultureInfo culture)
{
if (parameter is Type type && TypeDescriptor.GetConverter(type).IsValid(value))
return TypeDescriptor.GetConverter(type).ConvertFrom(value);
return null;
}
public object ConvertBack(object value,CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
XAML:
<Window x:Class="InvalidateCommandMvvmLight.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:InvalidateCommandMvvmLight"
xmlns:vm="clr-namespace:InvalidateCommandMvvmLight.ViewModel"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MyViewModel/>
</Window.DataContext>
<Window.Resources>
<local:ValueTypeConverter x:Key="ValueTypeConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox x:Name="tbText"
Text="Alle eendjes zwemmen in het water" VerticalAlignment="Center"
/>
<Button Content="Change Text"
Grid.Column="1"
Margin="5"
Padding="5,2"
Command="{Binding Path=CommandNumber}"
CommandParameter="{Binding Text,Converter={StaticResource ValueTypeConverter},ConverterParameter={x:Type sys:Int32},ElementName=tbNumber}">
</Button>
<TextBox Text="{Binding Number,Mode=OneWay}" IsReadOnly="True"
Grid.Row="1" Grid.Column="2" VerticalAlignment="Center"/>
</Grid>
</Window>
本文链接:https://www.f2er.com/1807455.html