Я хотел бы создать управляемый командой пользовательский элемент управления в 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,0,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), typeof(IconButton), default(ImageSource));
public ImageSource ButtonIcon
{
get => (ImageSource)GetValue(ButtonIconProperty);
set => SetValue(ButtonIconProperty, value);
}
public static readonly BindableProperty ButtonCommandProperty =
BindableProperty.Create("ButtonCommand", typeof(ICommand), typeof(IconButton), null, BindingMode.Default, null, 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), typeof(IconButton), 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]
(I удалите некоторые ненужные части)
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 с визуальным состоянием моего пользовательского элемента управления.
Любая помощь высоко ценится! :-) Спасибо!