WPF 中单选按钮的 CustomContainer 问题

发布于 2024-07-27 14:07:24 字数 3431 浏览 1 评论 0原文

我想创建一个仅包含单选按钮的自定义控件。 我想象它的使用方式如下:

<RadioButtonHolder Orientation="Horizontal">
<RadioButton>RadioButton 1</RadioButton>
<RadioButton>RadioButton 2</RadioButton>
<RadioButton>RadioButton 3</RadioButton>
<RadioButton> ...</RadioButton>
</RadioButtonHolder>

目前,我已经创建了一个自定义控件,它部分地执行此操作。 然而,它似乎保留了RadioButtons 的持续收集。 它会将这个 RadioButtons 集合添加到最后一个初始化的控件中。 有谁知道为什么会这样? 任何帮助深表感谢。

编辑: 我大概明白这是怎么回事了。 看起来,当对象初始化时,它将创建一个 RadioButtons 列表,其中包含所有 RadioButtons,然后将其附加到窗口中的所有 RadioButtonHolder 控件,如下所示孩子们。 最后一个控件用于显示项目。

但是我不确定如何防止这种情况,并且仅将内容本地化到每个控件。 所以如果我写:

<RadioButtonHolder Name="RBH1">
<RadioButton Name="RB1">RB 1</RadioButton>
<RadioButton Name="RB2">RB 2</RadioButton>
</RadioButtonHolder>
<RadioButtonHolder Name="RBH2">
<RadioButton Name="RB3">RB 3</RadioButton>
<RadioButton Name="RB4">RB 4</RadioButton>
</RadioButtonHolder>

RB1 & RB2 将显示在 RBH1RB3 & 中。 RB4 将在 RBH2 中显示为子项。

我的代码如下:

CustomControl.cs

using System.Collections.Generic;
using System.Windows;
using Sytem.Windows.Controls;
using System.Windows.Markup;

namespace RandomControl
{
[ContentProperty("Children")]
public class CustomControl1 : Control
{
   public static DependencyProperty ChildrenProperty = 
      DependencyProperty.Register("Children", typeof(List<RadioButton>),
      typeof(CustomControl1),new PropertyMetadata(new List<RadioButton>()));

   public List<RadioButton> Children
   {
       get { return (List<RadioButton>)GetValue(ChildrenProperty); }
       set { SetValue(ChildrenProperty, value); }
   }

    static CustomControl1()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), 
             new FrameworkPropertyMetadata(typeof(CustomControl1)));
    }
 }
}

Generic.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RandomControl">
 <Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsControl ItemsSource="{TemplateBinding Children}" 
                      Background="{TemplateBinding Background}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel></StackPanel>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
 </Style>
</ResourceDictionary>

I want to create a custom control which would only contain RadioButtons. I imagine it being used as follows:

<RadioButtonHolder Orientation="Horizontal">
<RadioButton>RadioButton 1</RadioButton>
<RadioButton>RadioButton 2</RadioButton>
<RadioButton>RadioButton 3</RadioButton>
<RadioButton> ...</RadioButton>
</RadioButtonHolder>

Currently, I have created a custom control which partially does this. However it seems to keep an ongoing collection of the RadioButtons. And it would add this collection of RadioButtons to the last control initialized. Does anyone know why this may be? Any help is much appreciated.

Edit:
I kind of figured out what was happening in this. It seems that when the object is initialized it will create a list of RadioButtons, which contains all the RadioButtons and then it would attach it to all the RadioButtonHolder controls in the window as children. And the last control gets to display the items.

However I'm not sure how to prevent this and only localize the content to each control. So that if I wrote:

<RadioButtonHolder Name="RBH1">
<RadioButton Name="RB1">RB 1</RadioButton>
<RadioButton Name="RB2">RB 2</RadioButton>
</RadioButtonHolder>
<RadioButtonHolder Name="RBH2">
<RadioButton Name="RB3">RB 3</RadioButton>
<RadioButton Name="RB4">RB 4</RadioButton>
</RadioButtonHolder>

RB1 & RB2 will be displayed in RBH1 and RB3 & RB4 will be displayed as children in RBH2.

My code is as follows:

CustomControl.cs

using System.Collections.Generic;
using System.Windows;
using Sytem.Windows.Controls;
using System.Windows.Markup;

namespace RandomControl
{
[ContentProperty("Children")]
public class CustomControl1 : Control
{
   public static DependencyProperty ChildrenProperty = 
      DependencyProperty.Register("Children", typeof(List<RadioButton>),
      typeof(CustomControl1),new PropertyMetadata(new List<RadioButton>()));

   public List<RadioButton> Children
   {
       get { return (List<RadioButton>)GetValue(ChildrenProperty); }
       set { SetValue(ChildrenProperty, value); }
   }

    static CustomControl1()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), 
             new FrameworkPropertyMetadata(typeof(CustomControl1)));
    }
 }
}

Generic.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RandomControl">
 <Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsControl ItemsSource="{TemplateBinding Children}" 
                      Background="{TemplateBinding Background}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel></StackPanel>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
 </Style>
</ResourceDictionary>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

断舍离 2024-08-03 14:07:24

WPF 旨在尽可能简单地使用,但它也很新,上手并不容易。

您所需要的只是像这样的一些xaml:

在您的窗口/页面/用户控件中添加资源

<Style x:Key="rbStyle" TargetType="RadioButton">
    <!--modify style to fit your needs-->
    <Setter Property="Margin" Value="2"/>
</Style>

<Style x:Key="rbStackPanelStyle" TargetType="StackPanel">
    <!--modify style to fit your needs-->
    <Setter Property="Orientation" Value="Vertical"/>
    <Setter Property="Margin" Value="2"/>
</Style>

然后在需要的地方声明您的“radioButtonHolder”:

<StackPanel x:Name="rbHolder1" Style="{StaticResource rbStackPanelStyle}">
    <RadioButton Style="{StaticResource rbStyle}">RadioButton 1</RadioButton>
    <RadioButton Style="{StaticResource rbStyle}">RadioButton 2</RadioButton>
    <RadioButton Style="{StaticResource rbStyle}">RadioButton 3</RadioButton>
    <RadioButton Style="{StaticResource rbStyle}">...</RadioButton>
</StackPanel>

根据您的问题,这应该适合您的需求。 不需要自定义控件。 许多进一步的修改可以适合其中的样式和模板。

希望这对您有帮助,干杯。

WPF is intended to be as simple to use as possible, but is also very new and not so easy to get in the start.

All you need is a some xaml like this:

In your window/page/usercontrol Resources add

<Style x:Key="rbStyle" TargetType="RadioButton">
    <!--modify style to fit your needs-->
    <Setter Property="Margin" Value="2"/>
</Style>

<Style x:Key="rbStackPanelStyle" TargetType="StackPanel">
    <!--modify style to fit your needs-->
    <Setter Property="Orientation" Value="Vertical"/>
    <Setter Property="Margin" Value="2"/>
</Style>

Then declare your "radioButtonHolder" wherever needed:

<StackPanel x:Name="rbHolder1" Style="{StaticResource rbStackPanelStyle}">
    <RadioButton Style="{StaticResource rbStyle}">RadioButton 1</RadioButton>
    <RadioButton Style="{StaticResource rbStyle}">RadioButton 2</RadioButton>
    <RadioButton Style="{StaticResource rbStyle}">RadioButton 3</RadioButton>
    <RadioButton Style="{StaticResource rbStyle}">...</RadioButton>
</StackPanel>

And that should fit your needs, according to your question. There's no need for a custom Control. And many further modifications could be fit inside styles and Templates therein.

Hope this was helpful, cheers.

我的鱼塘能养鲲 2024-08-03 14:07:24

我刚刚发现我做错了什么! 它就在我面前,但我没有看到。

这个问题的问题是我将 Children 设置为 DependencyProperty - 这意味着它将是静态的和全局的。 因此,整个 RadioButton 集合几乎可供窗口中的所有控件使用。 (事后看来,这可能就是为什么 StackPanel、Canvas 等没有 Children 属性作为 DependencyProperty)。
您可以在此处找到有关此内容的更多信息。

感谢 kek444 发布了一种更简单的方法来执行此操作。 :D

为了解决这个问题,您需要删除 DependencyProperty 并将 Children 声明为具有私有 set 的普通属性。

我修改了代码:

CustomControl.cs

using System.Collections.Generic;
using System.Windows;
using Sytem.Windows.Controls;
using System.Windows.Markup;

namespace RandomControl
{
    [ContentProperty("Children")]
    public class CustomControl1 : Control
    {
        private ObservableCollection<RadioButton> _children;
        private ItemsControl _control;

        public ObservableCollection<RadioButton> Children
        {
            get
            {
                if (_children == null)
                    _children = new ObservableCollection<RadioButton>();
                return _children;
            }
            private set { _children = value; }
        }

        static CustomControl1()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), 
             new FrameworkPropertyMetadata(typeof(CustomControl1)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _control = base.GetTemplateChild("PART_ItemsControl") 
                            as ItemsControl;

            // display the radio buttons
            if (_control != null)
                _control.ItemsSource = Children;
        }
    }
}

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RandomControl">
 <Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsControl Name="PART_ItemControl"  
                      Background="{TemplateBinding Background}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel></StackPanel>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
 </Style>
</ResourceDictionary>

I just found out what I was doing wrong! It was right in front of me and I didn't see it.

The issue with this problem was that I had Children set as a DependencyProperty - which means that it will be static and global. So the whole RadioButton collection was available pretty much to all the controls in the Window. (On hindsight which is probably why StackPanel, Canvas etc. doesn't have the Children property as a DependencyProperty).
You can find more information about this here.

Thanks kek444 for posting a simpler way to do this. :D

In order to fix this what you need to do is remove the DependencyProperty and declare Children as a normal property with a private set.

I have modified the code:

CustomControl.cs

using System.Collections.Generic;
using System.Windows;
using Sytem.Windows.Controls;
using System.Windows.Markup;

namespace RandomControl
{
    [ContentProperty("Children")]
    public class CustomControl1 : Control
    {
        private ObservableCollection<RadioButton> _children;
        private ItemsControl _control;

        public ObservableCollection<RadioButton> Children
        {
            get
            {
                if (_children == null)
                    _children = new ObservableCollection<RadioButton>();
                return _children;
            }
            private set { _children = value; }
        }

        static CustomControl1()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), 
             new FrameworkPropertyMetadata(typeof(CustomControl1)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _control = base.GetTemplateChild("PART_ItemsControl") 
                            as ItemsControl;

            // display the radio buttons
            if (_control != null)
                _control.ItemsSource = Children;
        }
    }
}

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:RandomControl">
 <Style TargetType="{x:Type local:CustomControl1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsControl Name="PART_ItemControl"  
                      Background="{TemplateBinding Background}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel></StackPanel>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
 </Style>
</ResourceDictionary>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文