Xamarin:视觉状态管理器和自定义属性
Xamarin: Visual State Manager and custom properties
我正在尝试使用 Visual State Manager 在自定义控件上设置自定义 属性,但到目前为止我运气不好。我的自定义控件只是一个带有附加可绑定 属性 的标签。
public class SelectableLabel : Label
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableLabel), false);
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
SetValue(IsSelectedProperty, value);
}
}
当控件进入 Selected
视觉状态时,我在 CollectionView 中使用此控件来切换 IsSelected
属性。
<CollectionView
x:Name="cv"
ItemsSource="{Binding Names}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedNames, Mode=TwoWay}"
VerticalOptions="Fill">
<CollectionView.ItemTemplate>
<DataTemplate>
<local:SelectableLabel
x:Name="lblName"
Text="{Binding First}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Selected">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableLabel>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
当我在 iOS 模拟器上 运行 时,当视觉状态变为 Selected
时,我没有看到 setter 被触发。如果我将 setter 中的 属性 更改为 BackgroundColor
或 Text
,我会看到预期的行为。该问题似乎特定于自定义 属性。我查看了 Setter.Property
的文档,它指出 Setter 可以应用于 IsSelected
所在的 BindableProperty
。是我做错了什么还是 VSM 不支持此功能?
编辑:此示例的 CollectionView
部分无关紧要。此代码也会出现同样的问题。
public class SelectableEntry : Entry
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false);
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
var color = value ? Color.LightBlue : Color.LightPink;
SetValue(IsSelectedProperty, value);
SetValue(BackgroundColorProperty, color);
}
}
}
这里是对应的XAML。当第一个自定义 Entry 控件获得焦点而第二个没有获得焦点时,背景颜色会发生变化。我也没有在控制台中看到我的 WriteLine
语句。
<local:SelectableEntry Text="First">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="LightBlue" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Focused">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="LightPink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<local:SelectableEntry Text="Second">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Focused">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
我已经尝试使用 Bindable Property 检查 属性 是否在 VisualStateManager
中被调用。很遗憾,即使在propertyChanged
方法中也无法调用。我想也许可绑定 属性 不能在 VisualStateManager
.
中工作
自定义Entry
代码如下:
public class SelectableEntry : Entry
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false ,propertyChanged:changedMethod);
private static void changedMethod(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine($"MDO: {oldValue}.IsSelected_set={newValue}");
}
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
var color = value ? Color.LightBlue : Color.LightPink;
SetValue(IsSelectedProperty, value);
SetValue(BackgroundColorProperty, color);
}
}
}
解决方案:
不过,有一个Attached Properties可以用在Style.Setters
中。然后你也可以在 VisualState.Setters
中尝试它。它也可以工作。
附上属性class代码如下:
public class SelectableEntryStyle
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.CreateAttached("IsSelected", typeof(bool), typeof(SelectableEntryStyle), false,propertyChanged:onChangedMethod);
private static void onChangedMethod(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine($"MDO:IsSelected_set={newValue}");
var color = (bool)newValue ? Color.LightBlue : Color.LightPink;
var entry = bindable as Entry;
entry.SetValue(Entry.BackgroundColorProperty, color);
}
public static bool GetIsSelected(BindableObject view)
{
return (bool)view.GetValue(IsSelectedProperty);
}
public static void SetIsSelected(BindableObject view, bool value)
{
view.SetValue(IsSelectedProperty, value);
}
}
Xaml的代码如下:
<local:SelectableEntry FontSize="18" Placeholder="Bindable Property">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<Entry Placeholder="Attached Property">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="local:SelectableEntryStyle.IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="local:SelectableEntryStyle.IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
当Entry
为Normal
或Focused
时控制台可以打印日志。
02-18 14:26:27.360 I/mono-stdout(26014): MDO:IsSelected_set=True
02-18 14:26:28.675 I/mono-stdout(26014): MDO:IsSelected_set=False
效果:
我正在尝试使用 Visual State Manager 在自定义控件上设置自定义 属性,但到目前为止我运气不好。我的自定义控件只是一个带有附加可绑定 属性 的标签。
public class SelectableLabel : Label
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableLabel), false);
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
SetValue(IsSelectedProperty, value);
}
}
当控件进入 Selected
视觉状态时,我在 CollectionView 中使用此控件来切换 IsSelected
属性。
<CollectionView
x:Name="cv"
ItemsSource="{Binding Names}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedNames, Mode=TwoWay}"
VerticalOptions="Fill">
<CollectionView.ItemTemplate>
<DataTemplate>
<local:SelectableLabel
x:Name="lblName"
Text="{Binding First}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Selected">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableLabel>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
当我在 iOS 模拟器上 运行 时,当视觉状态变为 Selected
时,我没有看到 setter 被触发。如果我将 setter 中的 属性 更改为 BackgroundColor
或 Text
,我会看到预期的行为。该问题似乎特定于自定义 属性。我查看了 Setter.Property
的文档,它指出 Setter 可以应用于 IsSelected
所在的 BindableProperty
。是我做错了什么还是 VSM 不支持此功能?
编辑:此示例的 CollectionView
部分无关紧要。此代码也会出现同样的问题。
public class SelectableEntry : Entry
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false);
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
var color = value ? Color.LightBlue : Color.LightPink;
SetValue(IsSelectedProperty, value);
SetValue(BackgroundColorProperty, color);
}
}
}
这里是对应的XAML。当第一个自定义 Entry 控件获得焦点而第二个没有获得焦点时,背景颜色会发生变化。我也没有在控制台中看到我的 WriteLine
语句。
<local:SelectableEntry Text="First">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="LightBlue" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Focused">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="LightPink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<local:SelectableEntry Text="Second">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x:Name="CommonStates">
<VisualState
x:Name="Normal">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Focused">
<VisualState.Setters>
<Setter
Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
我已经尝试使用 Bindable Property 检查 属性 是否在 VisualStateManager
中被调用。很遗憾,即使在propertyChanged
方法中也无法调用。我想也许可绑定 属性 不能在 VisualStateManager
.
自定义Entry
代码如下:
public class SelectableEntry : Entry
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create("IsSelected", typeof(bool), typeof(SelectableEntry), false ,propertyChanged:changedMethod);
private static void changedMethod(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine($"MDO: {oldValue}.IsSelected_set={newValue}");
}
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
Console.WriteLine($"MDO: {Text}.IsSelected_set={value}");
var color = value ? Color.LightBlue : Color.LightPink;
SetValue(IsSelectedProperty, value);
SetValue(BackgroundColorProperty, color);
}
}
}
解决方案:
不过,有一个Attached Properties可以用在Style.Setters
中。然后你也可以在 VisualState.Setters
中尝试它。它也可以工作。
附上属性class代码如下:
public class SelectableEntryStyle
{
public static readonly BindableProperty IsSelectedProperty = BindableProperty.CreateAttached("IsSelected", typeof(bool), typeof(SelectableEntryStyle), false,propertyChanged:onChangedMethod);
private static void onChangedMethod(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine($"MDO:IsSelected_set={newValue}");
var color = (bool)newValue ? Color.LightBlue : Color.LightPink;
var entry = bindable as Entry;
entry.SetValue(Entry.BackgroundColorProperty, color);
}
public static bool GetIsSelected(BindableObject view)
{
return (bool)view.GetValue(IsSelectedProperty);
}
public static void SetIsSelected(BindableObject view, bool value)
{
view.SetValue(IsSelectedProperty, value);
}
}
Xaml的代码如下:
<local:SelectableEntry FontSize="18" Placeholder="Bindable Property">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:SelectableEntry>
<Entry Placeholder="Attached Property">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="local:SelectableEntryStyle.IsSelected"
Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="local:SelectableEntryStyle.IsSelected"
Value="True" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
当Entry
为Normal
或Focused
时控制台可以打印日志。
02-18 14:26:27.360 I/mono-stdout(26014): MDO:IsSelected_set=True
02-18 14:26:28.675 I/mono-stdout(26014): MDO:IsSelected_set=False
效果: