views:

378

answers:

2

Hi

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>
A: 

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.

kek444
thanks for this! :) It does make sense to do it this way. Originally though, what I wanted for the holder to do was to overlay an InkPresenter on the RadioButtons so that I could do some fancy pen interaction.
Nilu
A: 

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