Search code examples
wpfbindinguser-controlsdependency-propertiesitemscontrol

Values in Int Array aren't being changed by Slider (itemsControl)


I have an int array, which I'm using to create a bunch of sliders in an ItemsControl. I'm using a two-way binding on the sliders, but the array never gets set (I put a breakpoint on the setter). All of this is in a UserControl.

UserControl XAML:

<ItemsControl ItemsSource="{Binding Values, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal" />
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <StackPanel Width="30" MaxWidth="30">
            <Slider Orientation="Vertical" Margin="5,0,0,0"
                                                Value="{Binding Path=., Mode=TwoWay}"
                                                Maximum="100"
                                                Minimum="-100"
                                                Height="100"/>
            <TextBox Text="{Binding Path=., Mode=TwoWay}" Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>

UserControl Codebehind:

    public int[] Values
    {
        get { return (int[])GetValue(ValuesProperty); }
        set { SetValue(ValuesProperty, value); }
    }
    public static readonly DependencyProperty ValuesProperty =
     DependencyProperty.Register("Values", typeof(int[]), typeof(Equalizer), new UIPropertyMetadata(new int[] { 0,0 }));

The UserControl is created in MainWindow, where it's fed the Values:

        <local:Equalizer Margin="50" Height="20" Width="100" VerticalAlignment="Top"
                     MyText="{Binding TextData, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}"
                     MyProperty="True" 
                     MinValue="{Binding MinValue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}"
                     MaxValue="{Binding MaxValue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}"
                     Values="{Binding Values, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}, Mode=TwoWay}"/>

Solution

  • Okay I found a way. It's not pretty, but it works..

    In the xaml I bind to a collection called "EQValues" like so:

                                <ItemsControl ItemsSource="{Binding EQValues, Mode=TwoWay}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Orientation="Horizontal" />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <StackPanel Width="30" MaxWidth="30">
                                            <Slider Orientation="Vertical" Margin="5,0,0,0"
                                                    Value="{Binding Value, Mode=TwoWay}"
                                                    Maximum="{Binding MaxValue, Mode=TwoWay}"
                                                    Minimum="{Binding MinValue, Mode=TwoWay}"
                                                    Height="100"/>
                                            <TextBox Text="{Binding Value, Mode=TwoWay}" 
                                                     Name="NumberTextBox" 
                                                     PreviewTextInput="NumberValidationTextBox"/>
                                        </StackPanel>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
    

    And then in the codebehind I change this collection to be equal to the int array, whenever the int array is changed:

            public int[] Values
        {
            get { return (int[])GetValue(ValuesProperty); }
            set { SetValueDp(ValuesProperty, value); }
        }
        public static readonly DependencyProperty ValuesProperty =
            DependencyProperty.Register("Values",
                typeof(int[]),
                typeof(Equalizer),
                new FrameworkPropertyMetadata(OnValuesChanged));
    
        public static void OnValuesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Get this specific Equalizer and call the UpdateCollection method
            Equalizer myUserControl = (Equalizer)d;
            myUserControl.UpdateCollection(e.NewValue as int[]);
        }
    
        void UpdateCollection(int[] intArr)
        {
            // Set EQValues to Values-Array, but as classes so ItemsControl can display it
            EQValues = new ObservableCollection<EQItem>();
            for (int i = 0; i < Values.Length; i++)
                EQValues.Add(new EQItem(Values[i], MinValue, MaxValue));
        }
    

    Then I save set the array by converting the collection back into an int-array:

            // Convert EQValues into array
            int[] arr = new int[EQValues.Count];
            for (int i = 0; i < EQValues.Count; i++)
            {
                arr[i] = EQValues[i].Value;
            }
            // Set values to new values
            Values = arr;
    

    Again, not pretty, but it works :).