简体   繁体   English

从包含的usercontrol设置窗口内某些控件的样式

[英]Set style for certain controls within window from contained usercontrol

I have an application with multiple usercontrols that are used within certain windows. 我有一个应用程序具有在某些窗口中使用的多个用户控件。 One of these usercontrols defines whether all other usercontrols in this window should allow editing, hence setting the IsEnabled property to False for all CheckBox es, ComboBox es and Button s. 其中一个用户控件定义此窗口中的所有其他用户控件是否应允许编辑,因此将所有CheckBox es, ComboBox es和Button s的IsEnabled属性设置为False However, TextBox es should allow to copying their text, hence should not be disabled, but only read-only. 但是, TextBox应该允许复制它们的文本,因此不应该被禁用,而只能是只读的。

I tried traversing the LogicalTree , but some self-built usercontrol does not have any property to disable them, but the controls contained within this usercontrol are only buttons and textboxes. 我尝试遍历LogicalTree ,但是一些自建的usercontrol没有任何属性来禁用它们,但是这个usercontrol中包含的控件只是按钮和文本框。 That's why I tried applying a style to all changable elements ( CheckBox , ComboBox , Button and TextBox ), but it won't work. 这就是为什么我尝试将样式应用于所有可变元素( CheckBoxComboBoxButtonTextBox ),但它不起作用。

In the usercontrol's Ressources section I definded some styles: 在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>

And in CodeBehind, after checking the condition, I tried the following: 在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'])

But nothing happened. 但什么都没发生。 What am I doing wrong? 我究竟做错了什么? Should I be doing it differently? 我应该采用不同的方式吗?

=EDIT 1================================================================== =编辑1 =============================================== ===================

I now tried the following, XAML: 我现在尝试了以下,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']

Suprisingly, even though the IsEnabled property is only set for ComboBox , everything is disabled. 令人惊讶的是,即使仅为ComboBox设置了IsEnabled属性,一切都被禁用。 And if I only set the TextBox.IsReadOnly property nothing happens. 如果我只设置TextBox.IsReadOnly属性没有任何反应。 Could someone explain this? 有人可以解释一下吗?

=EDIT 2================================================================== =编辑2 =============================================== ===================

I now also tried using a converter: 我现在也尝试使用转换器:

(XAML) (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>

(Converter) (转换器)

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;
    }
}

But again, everything is just disabled (or nothing happens if you use the variant commented out). 但同样,一切都被禁用(如果您使用已注释的变体,则没有任何反应)。

I got it working traversing the visual tree: 我让它遍历可视树:

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)

But I also would like to be able to later reset the changes to the original state. 但我也希望以后能够将更改重置为原始状态。 And that would mean I'd have to save all changes, which makes this far to complicated than it should be. 这意味着我必须保存所有更改,这使得这远远应该是复杂的。 I'm out of ideas. 我没有想法。

I don't think removing the style and adding a new one will notify the control to apply the new style. 我不认为删除样式并添加新样式将通知控件应用新样式。

You should set the style directly on the control like: 您应该直接在控件上设置样式,如:

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

Syntax might be different but I'm ac# guy. 语法可能有所不同,但我是ac#guy。

You might not be getting the resource by using self.Resources['disabledStyle'] (It usually does this when styles are defined in the control hierarchy). 您可能无法使用self.Resources['disabledStyle']获取资源(通常在控件层次结构中定义样式时执行此操作)。 it can give you null and may not notice it. 它可以给你null,可能不会注意到它。

try 尝试

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

FindResource() will give you error if it doesn't find the requested resource. 如果找不到请求的资源, FindResource()将给出错误。

Hi please try the next: 嗨,请尝试下一个:

XAML 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>

Attached properties code 附加属性代码

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);
    }
}

Helpers code 助手代码

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;
    }

}

**Short explanation:* **简短说明:*

Find all visual children of your root (window for example), scan them and perform action based on the child type. 查找根的所有可视子项(例如窗口),扫描它们并根据子类型执行操作。

Regards, 问候,

try this, 试试这个,
1.Add a boolean property say CanUserEdit to the usercontrol which controls what can be edited in other controls. 1.添加一个布尔属性,将CanUserEdit添加到usercontrol,后者控制可以在其他控件中编辑的内容。
2.Add datatrigger in other usercontrols and bind to CanUserEdit(2 datatriggers, 1 for combobox and the other for textbox). 2.在其他用户控件中添加datatrigger并绑定到CanUserEdit(2个数据触发器,1个用于组合框,另一个用于文本框)。

Define in it UserControl tag without key.this way it will affect all textboxes and comboboxes present in that usercontrol. 在其中定义没有key的UserControl标签。这将影响该usercontrol中存在的所有文本框和组合框。
you will also get a centralized control. 您还将获得集中控制权。

Sample Code:- 示例代码: -
Add CanUserEdit dependency property in every userControl. 在每个userControl中添加CanUserEdit依赖项属性。

        //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); }
    }

In UserControls that contain textboxes and comboboxes you would add following code to UserControl.Resources 在包含文本框和组合框的UserControl中,您可以将以下代码添加到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>

this will affect all the comboboxes and textboxes within that control. 这将影响该控件中的所有组合框和文本框。

And in your mainwindow,You will bind each UserControl's CanUserEdit Property to CanUserEdit property of UserControl which has control on edit. 在主窗口中,您将每个UserControl的CanUserEdit属性绑定到UserControl的CanUserEdit属性,该属性具有编辑控制权。
example(MainUserControl is the control that has textboxes and comboboxes): 示例(MainUserControl是具有文本框和组合框的控件):

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


Now all you have to do is toggle CanUserEdit property of UserControl that controls edit and all controls get affected. 现在,您所要做的就是切换UserControl的CanUserEdit属性,该属性控制编辑并且所有控件都受到影响。

I got it working in a not-so-elegant way, iterating over all controls and setting the property myself. 我让它以一种不那么优雅的方式工作,迭代所有控件并自己设置属性。 While doing so I save the infomartion about which controls I changed to be able to reset the UI to the original state. 在这样做的同时,我保存了关于我更改了哪些控件的信息,以便能够将UI重置为原始状态。 I'm not really happy with that, but it seems to work. 我对此并不满意,但似乎有效。 I would have prefered setting and un-setting some style, but I did not find a way to do so. 我宁愿设置和取消设置一些风格,但我没有找到办法。

Here's what I ended up using, but feel free to post something better. 这是我最终使用的内容,但随意发布更好的内容。 First the disabling part: 首先是禁用部分:

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)

And here's the part to "reset" the UI to it's original state: 以下是将UI“重置”为原始状态的部分:

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

the visited -set is just a local variable to avoid visiting controls more than once, which strangely happens eg for some grids. visited -set只是一个局部变量,可以避免不止一次访问控件,这种情况很奇怪,例如某些网格。 The disabledControls -set holds all controls that where not disabled and thus have been disabled by the code and have to be reset when the UI should reset itself to the original state. disabledControls -set保存所有未禁用的控件,因此已被代码禁用,并且必须在UI重置为原始状态时重置。

A simple way of achieving this scenario could be through publisher subscriber implementation. 实现此方案的一种简单方法可以是通过发布者订阅者实现。 Publishing a property state to other user controls and setting state of controls via binding/assigning that property to targeted controls is easy. 通过绑定/将该属性分配给目标控件,可以轻松地将属性状态发布到其他用户控件并设置控件状态。 I have achieved similar scenario via MvvmLight Messenger, I even disable some Ribbon commands based on some inner control states. 我通过MvvmLight Messenger实现了类似的方案,我甚至根据一些内部控制状态禁用了一些Ribbon命令。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM