Получить VisualState в Silverlight - PullRequest
2 голосов
/ 09 августа 2011

Я пытаюсь создать собственное текстовое поле для проверки и использую VisualStateManager для изменения состояний поля. Было бы очень полезно, если бы я мог прочитать текущее состояние текстового поля. Я не могу найти, как это сделать. Кто-нибудь знает, как получить текущий VisualState элемента (TextBox)?

Ответы [ 3 ]

3 голосов
/ 09 августа 2011

VisualStateManager не предоставляет механизм для получения визуального состояния. В Интернете есть решение, например В этом блоге описывается, как расширить VSM, чтобы сделать текущее состояние доступным.

2 голосов
/ 09 августа 2011

Колин прав, но это можно сделать, если не возражаешь быть немного дерзким.

Создайте новое приложение Silverlight. Включите следующий Xaml в MainPage: -

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="200" />
    </Grid.RowDefinitions>
    <TextBox x:Name="txt" Grid.Row="0"/>
    <ListBox x:Name="result" Grid.Row="1" />
</Grid>

Теперь получите код для класса методов расширения VisualTreeEnumeration из этого блога: Перечисление Visual Tree и включите его в приложение.

Теперь включите этот код в код позади MainPage: -

public partial class MainPage: UserControl
{
    DispatcherTimer timer = new DispatcherTimer();

    public MainPage()
    {
        InitializeComponent();
        Loaded += new RoutedEventHandler(MainPage_Loaded);
        Unloaded += new RoutedEventHandler(MainPage_Unloaded);
    }

    void MainPage_Unloaded(object sender, RoutedEventArgs e)
    {
        timer.Stop();
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        timer.Interval = TimeSpan.FromMilliseconds(500);
        timer.Tick += (s, args) =>
        {
            FrameworkElement innerControl = txt.Descendents(1).First() as FrameworkElement;

            result.ItemsSource = VisualStateManager.GetVisualStateGroups(innerControl).OfType<VisualStateGroup>()
                .Select(vsg => vsg.Name + " : " + (vsg.CurrentState != null ? vsg.CurrentState.Name : "<none>"));
        };
        timer.Start();    
    }
}

Запустите это и обратите внимание, что перечисленные визуальные состояния отслеживают наведение мыши и фокусированные состояния текстового поля.

Код выявляет первого визуального потомка элемента управления, к которому будет присоединено свойство VisualStateGroups. Затем он перечисляет каждый VisualStateGroup, перечисляющий его имя, и затем проверяет свойство CurrentState этой группы, чтобы определить, было ли выбрано состояние.

1 голос
/ 17 ноября 2014

На самом деле, 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 в нижней части контрольного кода. Таким образом, я могу хранить весь этот код в одном месте и в случае необходимости полностью удалить его, когда закончу тестирование (хм, еще лучше, я могу условно скомпилировать его).

В любом случае, надеюсь, это поможет кому-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...