Установить стиль для определенных элементов управления в окне из содержащегося пользовательского контроля

У меня есть приложение с несколькими пользовательскими элементами управления, которые используются в определенных окнах. Одно из этих usercontrols определяет, должны ли все другие пользовательские элементы управления в этом окне разрешать редактирование, поэтому для свойства IsEnabled для False для всех CheckBox es, ComboBox es и Button s. Тем не менее, TextBox es должны позволять копировать текст, поэтому его нельзя отключать, но только для чтения.

Я пробовал перемещаться по LogicalTree, но у некоторого автономного пользователя usercontrol нет никакого свойства, чтобы отключить их, но элементы управления, содержащиеся в этом usercontrol, являются только кнопками и текстовыми полями. Поэтому я попытался применить стиль ко всем изменяемым элементам (CheckBox, ComboBox, Button и TextBox), но это не сработает.

В разделе usercontrol Ressources я остановил некоторые стили:

<Style TargetType="Control" x:Key="disabledStyle">
    <Setter Property="IsEnabled" Value="False" />
</Style>
<Style TargetType="TextBox" x:Key="readOnlyStyle">
    <Setter Property="IsReadOnly" Value="True" />
</Style>

И в CodeBehind, после проверки условия, я попробовал следующее:

# windowOwner is the root window containing this usercontrol
for control in [Button, ComboBox, CheckBox]:
    if self.windowOwner.Resources.Contains(control):
        self.windowOwner.Resources.Remove(control)
    self.windowOwner.Resources.Add(control, self.Resources['disabledStyle'])

if self.windowOwner.Resources.Contains(TextBox):
    self.windowOwner.Resources.Remove(TextBox)
self.windowOwner.Resources.Add(TextBox, self.Resources['readOnlyStyle'])

Но ничего не произошло. Что я делаю не так? Должен ли я делать это по-другому?

= EDIT 1 =========================================================================================== ======================

Теперь я попробовал следующее: XAML:

<Style x:Key="disabledStyle">
    <!--<Setter Property="Button.IsEnabled" Value="False" />
    <Setter Property="CheckBox.IsEnabled" Value="False" />-->
    <Setter Property="ComboBox.IsEnabled" Value="False" />
    <Setter Property="TextBox.IsReadOnly" Value="True" />
</Style>

CodeBehind:

self.windowOwner.Style = self.Resources['disabledStyle']

Удивительно, хотя свойство IsEnabled установлено только для ComboBox, все отключено. И если я устанавливаю только свойство TextBox.IsReadOnly, ничего не происходит. Может кто-нибудь объяснить это?

= EDIT 2 ======================================================================================= ======================

Я также попытался использовать конвертер:

(XAML)

<Style TargetType="Control" x:Key="disabledStyle">
<Setter Property="IsEnabled" Value="False" />
<!--<Setter Property="Button.IsEnabled" Value="False" />
<Setter Property="CheckBox.IsEnabled" Value="False" />
<Setter Property="ComboBox.IsEnabled" Value="False" /> -->
    <Style.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource typeConverter}}" Value="True">
            <Setter Property="IsEnabled" Value="True" />
            <Setter Property="TextBox.IsReadOnly" Value="True" />
        </DataTrigger>
    </Style.Triggers>
</Style>

(конвертер)

public class TypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool res = value.GetType() == typeof(TextBox);
        return res;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {    // Don't need any convert back
        return null;
    }
}

Но опять же, все просто отключено (или ничего не происходит, если вы используете вариант, закомментированный).

У меня получилось работать, пересекая визуальное дерево:

visited = set()

def disableControls(control):
    visited.add(control)
    try:
        for childNumber in xrange(VisualTreeHelper.GetChildrenCount(control)):
            child = VisualTreeHelper.GetChild(control, childNumber)

            if hasattr(child, 'Content') and child.Content not in visited:
                disableControls(child.Content)
            if type(child) in [Button, ComboBox, CheckBox]:
                child.IsEnabled = False
            elif type(child) == TextBox:
                child.IsReadOnly = True
            elif child not in visited:
                disableControls(child)
    except:
        pass
disableControls(self.windowOwner)

Но я также хотел бы иметь возможность позже reset внести изменения в исходное состояние. И это означало бы, что я должен был бы сэкономить все изменения, что делает это намного сложнее, чем должно быть. У меня нет идей.

Ответ 1

У меня это работало не очень элегантно, повторяя все элементы управления и самостоятельно устанавливая свойство. При этом я сохраняю информацию о том, какие элементы управления я изменил, чтобы иметь возможность reset пользовательского интерфейса в исходное состояние. Я не очень доволен этим, но, похоже, это работает. Я бы предпочел установить и отменить какой-то стиль, но я не нашел способ сделать это.

Вот что я в конечном итоге использовал, но не стесняйтесь публиковать что-то лучше. Сначала отключить часть:

visited = set()

def disableControls(control):
    visited.add(control)

    for childNumber in xrange(VisualTreeHelper.GetChildrenCount(control)):
        child = VisualTreeHelper.GetChild(control, childNumber)

        # save the old state
        if type(child) in [Button, ComboBox, CheckBox] and child.IsEnabled:
            child.IsEnabled = False
            self.disabledControls.add(child)
        elif type(child) == TextBox and not child.IsReadOnly:
            child.IsReadOnly = True
            self.disabledControls.add(child)
        elif child not in visited:
            disableControls(child)
disableControls(self.windowOwner)

И вот часть к "reset" пользовательского интерфейса к оригинальному состоянию:

while self.disabledControls:
    child = self.disabledControls.pop()
    if type(child) in [Button, ComboBox, CheckBox]:
        child.IsEnabled = True
    elif type(child) == TextBox:
        child.IsReadOnly = False

visited -set - это просто локальная переменная, чтобы избежать контроля посещения более одного раза, что, как ни странно, происходит, например. для некоторых сеток. Элемент disabledControls содержит все элементы управления, которые не отключены и, следовательно, были отключены кодом и должны быть reset, когда пользовательский интерфейс должен reset сам в исходное состояние.

Ответ 2

Я не думаю, что удаление стиля и добавление нового будет уведомлять элемент управления, чтобы применить новый стиль.

Вы должны установить стиль непосредственно на элементе управления, например:

self.MyControl.Style = self.Resources['readOnlyStyle'] as Style

Синтаксис может отличаться, но я парень С#.

Ответ 3

Возможно, вы не получите ресурс, используя self.Resources['disabledStyle'] (Обычно это происходит, когда стили определены в иерархии управления). он может дать вам нулевое значение и может не заметить его.

попробовать

MyControl.Style = DirectCast(FindResource("labelStyle2"), Style)

FindResource() предоставит вам ошибку, если он не найдет запрошенный ресурс.

Ответ 4

Привет, пожалуйста, попробуйте следующее:

XAML

<Window x:Class="ListViewWithCanvasPanel.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:listViewWithCanvasPanel="clr-namespace:ListViewWithCanvasPanel"
    Title="MainWindow" Height="350" Width="525" x:Name="This" ResizeMode="CanResize" 
    listViewWithCanvasPanel:Attached.AreChildrenEnabled = "true"><!--put your content here--></Window>

Код прикрепленных свойств

public class Attached
{
    public static readonly DependencyProperty AreChildrenEnabledProperty = DependencyProperty.RegisterAttached("AreChildrenEnabled", typeof (bool), typeof (Attached), new PropertyMetadata(default(bool), AreChildrenEnabledPropertyChangedCallback));

    private static void AreChildrenEnabledPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        var val = (bool) args.NewValue;
        if (val == false)
        {
            var visual = dependencyObject as FrameworkElement;
            if (visual == null) return;
            visual.Loaded -= VisualOnLoaded;
            visual.Unloaded -= VisualOnUnloaded;
        }
        else
        {
            var visual = dependencyObject as FrameworkElement;
            if(visual == null) return;
            visual.Loaded += VisualOnLoaded;
            visual.Unloaded += VisualOnUnloaded;
        }
    }

    private static void VisualOnUnloaded(object sender, RoutedEventArgs e)
    {
        var visual = sender as FrameworkElement;
        if (visual == null) return;
        visual.Loaded -= VisualOnLoaded;
    }

    private static void VisualOnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        var visual = sender as FrameworkElement;
        if (visual == null) return;
        var list = visual.GetAllVisualChildren();
        Debug.WriteLine("children count on loading: {0}", list.Count);
        var actionOnChildrenLoading = GetActionOnEachLoadedVisualChild(visual);
        if(actionOnChildrenLoading == null) return;
        list.ForEach(o =>
        {
            var combo = o as ComboBox;
            if (combo != null)
            {
                combo.IsEnabled = false;
            }

            var button = o as Button;
            if (button != null)
            {
                button.IsEnabled = false;
            }

            var textBlock = o as TextBlock;
            if (textBlock != null)
            {
                textBlock.IsEnabled = false;
            }

            var cb = o as CheckBox;
            if (cb != null)
            {
                cb.IsEnabled = false;
            }

            var textBox = o as TextBox;
            if (textBox == null) return;
            textBox.IsEnabled = true;
            textBox.IsReadOnly = true;
        });
    }

    public static readonly DependencyProperty ActionOnEachLoadedVisualChildProperty = DependencyProperty.RegisterAttached(
        "ActionOnEachLoadedVisualChild", typeof (Action<DependencyObject>), typeof (Attached), new PropertyMetadata(default(Action<DependencyObject>)));

    public static void SetActionOnEachLoadedVisualChild(DependencyObject element, Action<DependencyObject> value)
    {
        element.SetValue(ActionOnEachLoadedVisualChildProperty, value);
    }

    public static Action<DependencyObject> GetActionOnEachLoadedVisualChild(DependencyObject element)
    {
        return (Action<DependencyObject>) element.GetValue(ActionOnEachLoadedVisualChildProperty);
    }

    public static bool GetAreChildrenEnabled(UIElement element)
    {
        return (bool) element.GetValue(AreChildrenEnabledProperty);
    }

    public static void SetAreChildrenEnabled(UIElement element, bool value)
    {
        element.SetValue(AreChildrenEnabledProperty, value);
    }
}

Код помощников

public static class VisualTreeHelperExtensions
{
    public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
    {
        while (true)
        {
            //get parent item
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            //we've reached the end of the tree
            if (parentObject == null) return null;

            //check if the parent matches the type we're looking for
            T parent = parentObject as T;
            if (parent != null)
                return parent;
            child = parentObject;
        }
    }

    public static List<DependencyObject> GetAllVisualChildren(this DependencyObject parent)
    {
        var resultedList = new List<DependencyObject>();
        var visualQueue = new Queue<DependencyObject>();
        visualQueue.Enqueue(parent);

        do
        {
            var depObj = visualQueue.Dequeue();
            var childrenCount = VisualTreeHelper.GetChildrenCount(depObj);

            for (int i = 0; i < childrenCount; i++)
            {
                var v = VisualTreeHelper.GetChild(depObj, i);
                visualQueue.Enqueue(v);
            }

            resultedList.Add(depObj);
        } while (visualQueue.Count > 0);

        resultedList.RemoveAt(0);
        return resultedList;
    }

}

** Краткое описание: *

Найдите все визуальные дочерние элементы вашего корня (например, окно), сканируйте их и выполните действие на основе дочернего типа.

Привет,

Ответ 5

попробуйте это,
1. Добавьте логическое свойство: CanUserEdit для usercontrol, который контролирует то, что можно редактировать в других элементах управления.
2. Добавьте datatrigger в другие usercontrols и привяжите к CanUserEdit (2 datatriggers, 1 для combobox и другое для текстового поля).

Определите в нем тег UserControl без ключа. Таким образом, это повлияет на все текстовые поля и комбобокс, присутствующие в этом usercontrol.
вы также получите централизованный контроль.

Пример кода: -
Добавьте свойство зависимости CanUserEdit в каждый userControl.

        //Replace MainUserControl with your control name
 public static readonly DependencyProperty CanUserEditProperty =
 DependencyProperty.Register("CanUserEdit", typeof(bool),
 typeof(MainUserControl));

    public bool CanUserEdit
    {
        get { return (bool)GetValue(CanUserEditProperty); }
        set { SetValue(CanUserEditProperty, value); }
    }

В UserControls, которые содержат текстовые поля и комбобокс, вы добавили бы следующий код в UserControl.Resources

<UserControl.Resources>
    <Style TargetType="TextBox">
        <Setter Property="IsReadOnly" Value="False"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding CanUserEdit}" Value="false">
                <Setter Property="IsReadOnly" Value="True"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    <Style TargetType="ComboBox">
        <Setter Property="IsEnabled" Value="True"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding CanUserEdit}" Value="false">
                <Setter Property="IsEnabled" Value="False"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>

это повлияет на все поля со списком и текстовые поля внутри этого элемента управления.

И в вашем главном окне вы привяжете каждое свойство CanUserEdit UserControl к свойству CanUserEdit UserControl, у которого есть контроль над редактированием.
example (MainUserControl - это элемент управления, который имеет текстовые поля и поля со списком):

<local:MainUserControl  CanUserEdit="{Binding CanUserEdit,ElementName=CanUserEditControl}"  />


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

Ответ 6

Простым способом достижения этого сценария может быть реализация подписчика издателя. Публикация состояния свойства на другие пользовательские элементы управления и настройка состояния элементов управления посредством привязки/назначения этого свойства целевым элементам управления легко. Я достиг такого же сценария через MvvmLight Messenger, даже отключил некоторые команды ленты, основанные на некоторых внутренних состояниях управления.