0

I have a problem with button bindings in user control. This thing bugs me for days and I can't seem to get it working right.

The idea is to have several buttons in user control(which can be added dynamically). Every button can have a different title and a different value. Buttons should be able to disable/enable each others, example if "Option 1" is clicked "Option 9" should be disabled. User Control Example

User control is a simple StackPanel with multiple buttons as seen on the image. I've implemented a DependencyProperty as int named Value. When any of the button is clicked, Value is changed by the value of the button. I've binded that Value to model value. When any button is clicked model value changes. But, when I change model value, control value does not change. How to do it two way correctly? How to select only one button which is in the model? I've also tried implementing INotifyPropertyChanged into my user control but it didn't work. I've also tried some other things I've read here, but it didn't work.

In short, I need a group of buttons which have different values and map their own values into one value if clicked. I should be able to get/set that value from my model.

I also need some kind of way to enable/disable buttons depending on which button is clicked. As I wrote already, if button "Option 1" is clicked it should disable "Option 9" button. Any way to do that via data binding? Value converter?

Thnx!

Control

namespace TestApp
{
    public partial class SingleOptionControl : UserControl
    {
        private static SolidColorBrush _selectedColorBrush = new SolidColorBrush(Color.FromRgb(0, 120, 200));

        private SingleOptionModel _model;

        private List<Button> _buttons;

        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(SingleOptionControl), new PropertyMetadata(null));

        public SingleOptionControl()
        {
            InitializeComponent();
            _buttons = new List<Button>();
            this.DataContext = this;
        }

        private void buttonIndex_Click(object sender, RoutedEventArgs e)
        {
            Button button = (Button)sender;
            ButtonOption option = (ButtonOption)button.Tag;
            int index = option.Index;
            Console.WriteLine("Current index = {0} | Value = {1}", index, option.Value);

            // Update the Value
            Value = option.Value;

            // Deselect
            DeselectButtons();
            // Select
            SelectButton(index);
        }

        private void SelectButton(int index)
        {
            Button button = _buttons[index];
            button.Foreground = _selectedColorBrush;
            button.FontWeight = FontWeights.Bold;
            button.FontSize = 14.0;
        }

        private void DeselectButtons()
        {
            foreach (Button button in _buttons)
            {
                button.Foreground = Brushes.Black;
                button.FontWeight = FontWeights.Normal;
                button.FontSize = 12.0;
            }
        }

        public void Create(SingleOptionModel model)
        {
            _model = model;

            labelTitle.Content = model.Title;

            int index = 0;
            List<ButtonSection> sections = model.Sections;
            foreach (ButtonSection section in sections)
            {
                List<ButtonOption> options = section.Options;
                foreach (ButtonOption option in options)
                {
                    option.Index = index;
                    Button button = createButton(option);
                    button.Click += buttonIndex_Click;
                    stackPanel1.Children.Add(button);
                    _buttons.Add(button);
                    index++;
                }
                Separator separator = createSeparator();
                stackPanel1.Children.Add(separator);
            }

            labelDefaultValue.Content = model.DefaultOption.Value;

            Value = model.DefaultOption.Value;
        }

        private Button createButton(ButtonOption option)
        {
            Button button = new Button();
            button.FontSize = 12.0;
            button.Content = option.Title;
            button.Tag = option;
            button.Height = 36;
            button.MinWidth = 48;
            button.Padding = new Thickness(6, 0, 6, 0);
            button.Margin = new Thickness(1, 0, 1, 0);
            return button;
        }

        private Separator createSeparator()
        {
            Separator separator = new Separator();
            separator.Width = 6;
            separator.Background = null;
            return separator;
        }
    }
}

UserControl

<UserControl x:Class="TestApp.SingleOptionControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d"
             d:DesignHeight="167" d:DesignWidth="800">
    <Grid Name="gridMain" Height="96">
        <Label Height="30" HorizontalAlignment="Stretch" Name="labelTitle" VerticalAlignment="Top" Background="{x:Null}" VerticalContentAlignment="Center" FontSize="18" FontStyle="Normal" FontWeight="Bold" Padding="10,0,0,0" Margin="0,2,0,0" />
        <StackPanel Height="40" HorizontalAlignment="Stretch" Margin="10,32,0,0" Name="stackPanel1" VerticalAlignment="Top" Orientation="Horizontal"></StackPanel>
        <Label Content="Default:" Height="28" HorizontalAlignment="Left" Margin="4,68,0,0" Name="label2" VerticalAlignment="Top" Width="50" Padding="10,0,0,0" VerticalContentAlignment="Center" />
        <Label Content="Value" Height="28" HorizontalAlignment="Left" Margin="60,68,0,0" Name="labelDefaultValue" VerticalAlignment="Top" Width="384" Padding="0" VerticalContentAlignment="Center" />
        <Rectangle Height="1" HorizontalAlignment="Stretch" Name="rectangle1" Stroke="{x:Null}" VerticalAlignment="Top" Fill="#14000000" />
    </Grid>
</UserControl>

Class TestModel

namespace TestApp
{
    public class TestModel : INotifyPropertyChanged
    {
        private byte _testValue;
        public byte TestValue
        {
            get { return _testValue; }
            set
            {
                _testValue = value;
                OnPropertyChanged("TestValue");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
H.B.
  • 166,899
  • 29
  • 327
  • 400
mbozic
  • 1
  • 3
  • instead of a usercontrol with buttons, try use a list with radiobuttons. examples here: http://stackoverflow.com/questions/805015/data-bound-radio-button-list-in-wpf – ASh Sep 12 '16 at 10:37
  • No good, I must have more flexibility. Example, in some cases I must have 2 or more buttons selected at the same time. It's trivial without data binding. – mbozic Sep 13 '16 at 05:59
  • if multiple selection required, `CheckBox`es can be used instead of `RadioButton` – ASh Sep 13 '16 at 06:39
  • Sure, but my client desires regular buttons which can behave as RadioButtons and CheckBoxes. I've put an image at the beginning of the text how it should look. Control should be something similar to Segmented Buttons control from iOS for start. - Control should have multiple buttons and change one value with it, that one value should be binded to model value. – mbozic Sep 13 '16 at 06:53
  • wpf control apperance is defined in its `Template`. it is very common to create custom styles and templates for different controls. so RadioButton/CheckBox can look like a Button and display values provided by Binding[s]. "The idea is to have several buttons in user control(which can be added dynamically)" sound for me like a task for `ItemsControl` (or a `ListBox` which support selection out-of-box). I wouldn't replace them with a lot of code-behind. please take a look at my examples here: http://stackoverflow.com/questions/35060859/wpf-radio-button-with-image – ASh Sep 13 '16 at 07:01
  • Thank you for the info, managed to work something out. – mbozic Sep 20 '16 at 07:12

0 Answers0