На самом деле, VisualStateManager
поддерживает механизм для этого. Я сам очень нуждался в этом и попал в SO & Q & A. Ни одно из этих решений мне не подошло, поэтому я пошел искать и кодировать свое собственное. Вот код ниже, наслаждайтесь!
Во-первых, нам нужно очень простое управление. Давайте использовать проверенный временем настраиваемый элемент управления кнопками (да, скучно, я знаю).
public class MyCustomButton : System.Windows.Controls.Button
{
static MyCustomButton()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(MyCustomButton),
new FrameworkPropertyMetadata(typeof(MyCustomButton)));
}
public MyCustomButton()
: base()
{
}
#region CurrentCommonVisualState Property
private static readonly DependencyPropertyKey CurrentCommonVisualStatePropertyKey =
DependencyProperty.RegisterReadOnly(
"CurrentCommonVisualState",
typeof(string),
typeof(MyCustomButton));
public static readonly DependencyProperty CurrentCommonVisualStateProperty =
MyCustomButton.CurrentCommonVisualStatePropertyKey.DependencyProperty;
[Category("Miscellaneous")]
[Bindable(true)]
[ReadOnly(true)]
public string CurrentcommonVisualState
{
get { return (string)base.GetValue(CurrentCommonVisualStateProperty); }
protected set { base.SetValue(CurrentCommonVisualStatePropertyKey, value); }
}
#endregion CurrentCommonVisualState Property
#region VisualStateManager Methods
protected T GetTemplateChild<T>(string name) where T : DependencyObject
{
return GetTemplateChild(name) as T;
}
// In WPF, in order to use the VSM, the VSM must be the first child of
// your root control template element and that element must be derived
// from System.Windows.Controls.Panel (e.g., like a Grid control).
//
// This restriction no longer exists with Windows Store apps.
//
// But this is why the first parameter to this method is of type
// Panel.
protected VisualStateGroup GetVisualStateGroup(Panel visualStateManagerElement,
string visualStateGroupName)
{
if (visualStateManagerElement == null)
{
return null;
}
VisualStateGroup result = null;
var visualStateGroups =
VisualStateManager.GetVisualStateGroups(visualStateManagerElement);
foreach (VisualStateGroup vsg in visualStateGroups)
{
if (vsg.Name == visualStateGroupName)
{
result = vsg;
break;
}
}
return result;
}
// When the control changes visual state, get the name of the
// current visual state from the CommonStates visual state group
// and set the CurrentCommonVisualState property.
//
// Then, you could potentially bind to that property.
internal override void ChangeVisualState(bool useTransitions)
{
// Using IL Spy, look at PresentationFramework.dll and see how
// MS implements this method. We're going to add some
// functionality here to get the current visual state.
base.ChangeVisualStat(useTransitions);
Panel templateRoot = this.GetTemplateChild<Panel>("TemplateRoot");
VisualStateGroup vsg = this.GetVisualStateGroup(templateRoot, "CommonStates");
if (vsg != null && vsg.CurrentState != null)
{
this.CurrentCommonVisualState = vsg.CurrentState.Name;
}
}
}
Теперь давайте представим, что у вас есть шаблон элемента управления специально для этого нового элемента управления MyCustomButton
, который вы разрабатываете, и во время разработки вы пытаетесь отладить логику VSM. Имея этот код в своем контроле, в своем тестовом приложении вы можете привязать к свойству CurrentCommonVisualState
:
<!-- Imagine you have some other WPF XAML markup above for your MainWindow test application -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid>
<MyCustomButton x:Name="MyCustomButton"
Grid.Row="0"
Width="75",
Height="23"
Content="My Button" />
<TextBox x:Name="TestCurrentCommonVisualStateName"
Grid.Row="1"
Width="100"
Height="20"
Text="{Binding CurrentCommonVisualState, Mode=OneWay, ElementName=MyCustomButton}" />
</Grid>
Вот и все, теперь вы можете определить, какой ток VisualState
выходит из CommonStates
VisualStateGroup
. Вам понадобится одно свойство на VisualStateGroup
, которое вы хотите «наблюдать» во время разработки.
Для моей собственной разработки пользовательского элемента управления я просто поместил оператор области #region Temp Testing Code
в нижней части контрольного кода. Таким образом, я могу хранить весь этот код в одном месте и в случае необходимости полностью удалить его, когда закончу тестирование (хм, еще лучше, я могу условно скомпилировать его).
В любом случае, надеюсь, это поможет кому-то еще.