我想在Xamarin.Forms中创建一个命令驱动的自定义控件。它应该像普通按钮一样工作,不同的状态表示禁用。一切工作都很好,但是我无法使用该命令的CanExecute属性来获取绑定命令来驱动控件的可视状态。我尝试使用ButtonCommandPropertyChanged,但是当我在视图模型中调用ChangeCanExecute()时并没有触发。
我当然可以在控件上引入另一个属性来更改状态,但是我认为应该可以使用CanExecute status命令来做到这一点(而且应该更加优雅……)
[IconButton.xaml]
<?xml version="1.0" encoding="UTF-8"?>
<ContentView WidthRequest="180" HeightRequest="90"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="TestStyle.Controls.IconButton">
<ContentView.Content>
<Frame x:Name="MyFrame" Style="{StaticResource StandardFrameStyle}" Margin="5" Padding="10,12,10,10">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BorderColor" Value="Black"></Setter>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BorderColor" Value="Red"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Image x:Name="InnerImage"
Grid.Row="0"
HorizontalOptions="Center"
Aspect="AspectFit"
Margin="0,1"
HeightRequest="35"/>
<Label x:Name="InnerLabel"
Grid.Row="1"
VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"
TextColor="{StaticResource TextColor}"
FontSize="12"
LineHeight="0.9"
MaxLines="2"/>
</Grid>
</Frame>
</ContentView.Content>
[IconButton.xaml.cs]
using System;
using System.Diagnostics;
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestStyle.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class IconButton : ContentView
{
public EventHandler Clicked;
public static readonly BindableProperty ButtonTextProperty =
BindableProperty.Create("ButtonText",typeof(string),typeof(IconButton),default(string));
public string ButtonText
{
get => ((string)Getvalue(ButtonTextProperty))?.ToUpper();
set => Setvalue(ButtonTextProperty,value);
}
public static readonly BindableProperty ButtonIconProperty =
BindableProperty.Create("ButtonIcon",typeof(ImageSource),default(ImageSource));
public ImageSource ButtonIcon
{
get => (ImageSource)Getvalue(ButtonIconProperty);
set => Setvalue(ButtonIconProperty,value);
}
public static readonly BindableProperty ButtonCommandProperty =
BindableProperty.Create("ButtonCommand",typeof(ICommand),null,BindingMode.Default,ButtonCommandPropertyChanged,ButtonCommandPropertyChanged);
private static void ButtonCommandPropertyChanged(BindableObject bindable,object oldvalue,object newvalue)
{
Debug.WriteLine($"oldValue: ${oldvalue}");
Debug.WriteLine($"newValue: ${newvalue}");
}
public ICommand ButtonCommand
{
get => (ICommand)Getvalue(ButtonCommandProperty);
set
{
Setvalue(ButtonCommandProperty,value);
Debug.WriteLine("ButtonCommand wurde gesetzt!!");
}
}
public static readonly BindableProperty commandparameterProperty =
BindableProperty.Create("commandparameter",typeof(object),null);
public object commandparameter
{
get => (object)Getvalue(commandparameterProperty);
set => Setvalue(commandparameterProperty,value);
}
public IconButton()
{
InitializeComponent();
this.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(() =>
{
Clicked?.Invoke(this,EventArgs.Empty);
if (ButtonCommand == null) return;
if (ButtonCommand.CanExecute(commandparameter))
ButtonCommand.Execute(commandparameter);
})
});
}
}
}
[FirstPage.xaml]
(我删除了一些不相关的部分)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:TestStyle.Controls;assembly=TestStyle"
xmlns:viewModels="clr-namespace:TestStyle.ViewModels;assembly=TestStyle"
x:DataType="viewModels:ExampleViewModel"
mc:Ignorable="d"
x:Class="TestStyle.Views.FirstPage"
BackgroundColor="{StaticResource LightBgColor}"
Title="Test"
NavigationPage.HasnavigationBar="false">
<ContentPage.BindingContext>
<viewModels:ExampleViewModel/>
</ContentPage.BindingContext>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:IconButton Grid.Row="1" Grid.Column="0"
ButtonText="Enable"
ButtonIcon="shuffle_gr.png"
ButtonCommand="{Binding EnableCommand}" />
<controls:IconButton Grid.Row="1" Grid.Column="1"
ButtonText="Disable"
ButtonIcon="clock_gr.png"
ButtonCommand="{Binding DisableCommand}" />
<controls:IconButton Grid.Row="2" Grid.Column="1"
ButtonText="Personal anfordern"
ButtonIcon="select_gr.png"
ButtonCommand="{Binding MyDynamicCommand}" />
<Label Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding StatusMessage}" HorizontalTextAlignment="Center"/>
</Grid>
</ContentPage>
[ExampleViewModel.cs]
(我删除了一些不相关的部分)
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using TestStyle.Models;
using TestStyle.Views;
using Xamarin.Forms;
namespace TestStyle.ViewModels
{
public class ExampleViewModel : INotifyPropertyChanged
{
private string _statusMessage;
private bool _buttonState;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
}
public string StatusMessage
{
get => _statusMessage;
set
{
if (_statusMessage == value) return;
_statusMessage = value;
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("StatusMessage"));
}
}
public bool ButtonState
{
get
{
return _buttonState;
}
set
{
_buttonState = value;
OnPropertyChanged(nameof(ButtonState));
}
}
public ICommand MyDynamicCommand { get; }
public ICommand EnableCommand { get; }
public ICommand DisableCommand { get; }
public ExampleViewModel()
{
SetButtonState(true);
MyDynamicCommand = new Command(() => StatusMessage = $"This is dynamic at {DateTime.Now.ToLongTimeString()}",() => ButtonState);
EnableCommand = new Command(() => SetButtonState(true));
DisableCommand = new Command(() => SetButtonState(false));
}
private void SetButtonState(bool newState)
{
ButtonState = newState;
var myCmd = ((Command) MyDynamicCommand);
if (myCmd != null)
{
myCmd.ChangeCanExecute(); // I think my control should be notified here!?
}
}
}
}
我不知道如何将CanExecute的更改连接到我的自定义控件的可视状态。
任何受到高度赞赏的帮助! :-)谢谢!