none
binding usercontrol elements properties

    Question

  • I am desperatly trying to understand bindings in WPF, I would like to understand how I can bind the elements of 2 Usercontrols to each other, so that if I change the value of the slider the circle strokethickness changes and if I change the circle strokethickness the slider changes. I have some code that hopefully explains what I want to understand better. TIA Mike

    <Window x:Class="WpfDependencyTest.MainWindow"
    
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    
      xmlns:local="clr-namespace:WpfDependencyTest" 
    
      xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    
      xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Layout;assembly=Microsoft.Expression.Interactions" 
    
      Title="MainWindow" Height="350" Width="525">
    
     <Grid>
    
      <local:UcCircleText ></local:UcCircleText>
    
      <StackPanel Height="Auto" HorizontalAlignment="Right" x:Name="stackPanel1" VerticalAlignment="Bottom" Width="Auto" Background="Aqua">
    
       <local:UcSlider Height="150" Width="150"></local:UcSlider>
    
       <i:Interaction.Behaviors>
    
        <ei:MouseDragElementBehavior/>
    
       </i:Interaction.Behaviors>
    
      </StackPanel>
    
      
    
     </Grid>
    
    </Window>
    <UserControl x:Class="WpfDependencyTest.UcCircleText"
    
        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"
    
        xmlns:local="clr-namespace:WpfDependencyTest"
    
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    
        xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Layout;assembly=Microsoft.Expression.Interactions" 
    
        mc:Ignorable="d" 
    
        d:DesignHeight="300" d:DesignWidth="300">
    
     <UserControl.Resources>
    
      <local:UcSlider x:Key="ucslider"></local:UcSlider>
    
     </UserControl.Resources>
    
     <i:Interaction.Behaviors>
    
      <ei:MouseDragElementBehavior/>
    
     </i:Interaction.Behaviors>
    
     <Grid>
    
      <Ellipse Height="100" x:Name="ellipse1" Stroke="Black" Width="100" Fill="Coral" />
    
     </Grid>
    
    </UserControl>
    
    <UserControl x:Class="WpfDependencyTest.UcSlider"
    
        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" 
    
        xmlns:local="clr-namespace:WpfDependencyTest"
    
        xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=PresentationFramework"
    
        mc:Ignorable="d" 
    
        d:DesignHeight="300" d:DesignWidth="300">
    
     <UserControl.Resources>
    
      <local:UcCircleText x:Key="circle"></local:UcCircleText>
    
     </UserControl.Resources>
    
     <Controls:Grid>
    
      <Slider Height="23" x:Name="slider1" Width="100" Minimum="2" Maximum="10" />
    
     </Controls:Grid>
    
    </UserControl>
    Sunday, November 28, 2010 7:00 PM

Answers

  • You can't do this directly because that would break the encapsulation of the UserControls.   The fact that UC1 contains a slider is an implementation detail that should be hidden from the world outside UC1 - in particular it's hidden from UC2.   Similarly, the ruler inside UC2 is hidden from UC1.

    But you can do it indirectly by defining a new depencency property on UC1, let's call it MyValue.  Inside UC1, bind the slider's Value to the UC's MyValue property.   Similarly, define a new property MyOpacity on UC2, and bind the ruler's Opacity to the UC's MyOpacity.    Now you can bind these new properties to get the effect you want:

    <UC2 x:Name="UC2"/>
    <UC1 x:Name="UC1"  MyValue="{Binding ElementName=UC2, Path=MyOpacity}"/>

    • Marked as answer by surfluds Tuesday, November 30, 2010 3:40 PM
    Tuesday, November 30, 2010 5:55 AM
  • Yes, Sam is exactly correct on this:

    In your code behind of the UC with the Slider you have to do something similar to this.

    using System;
    using System.Windows.Controls;
    using System.ComponentModel;
    
    namespace TestListBox
    {
      /// <summary>
      /// Interaction logic for UCOne.xaml
      /// </summary>
      public partial class UCOne : UserControl, INotifyPropertyChanged
      {
        public UCOne()
        {
          InitializeComponent();
          _SliderValue = 1;
        }
        private double _SliderValue;
        public double SliderValue
        {
          get { return _SliderValue;}
          set { _SliderValue = value;
             PropChanged("SliderValue");      }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void PropChanged(String PropName) {
          if (PropertyChanged != null) { 
            PropertyChanged(this, new PropertyChangedEventArgs(PropName));
          }
        }
      }
    }
    

    Then in the mark up on the main window where UC1 and UC2 are located you need to "Bind" to this property.

    <local:UCOne x:Name="UCOne" Width="200"></local:UCOne>
    <local:UCTwo Width="200" 
     Opacity="{Binding ElementName=UCOne, Path=SliderValue}"> 
    

    This will solve the problem...

    The key to remember is that any property needing to be exposed is only seen by WPF when a DependencyProperty is exposed.  In this case we used the INotifyProperty changed event and the appropriate Getter/Setter methods to expose these properties.  WPF then will see the Getter/Setters which allows you to see them in the XAML Designer by clicking on the opacticy property and choosing the appropriate Bindings.  This is confusing to a person just learning, but over time it becomes crystal clear.


    Javaman
    • Marked as answer by surfluds Tuesday, November 30, 2010 3:40 PM
    Tuesday, November 30, 2010 2:26 PM

All replies

  • You can binding a property of one control with another property of another control. If the original property change the target will change too

    For example:

     

    <Window
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Width="460" Height="200"
     Title="Binding the Properties of Two Controls">
    
     <Window.Resources>
      <Style TargetType="TextBlock">
       <Setter Property="FontSize" Value="16"/>
       <Setter Property="FontWeight" Value="Bold"/>
       <Setter Property="DockPanel.Dock" Value="Top"/>
       <Setter Property="HorizontalAlignment" Value="Center"/>
      </Style>
      <Style TargetType="Canvas">
       <Setter Property="Height" Value="50"/>
       <Setter Property="Width" Value="50"/>
       <Setter Property="Margin" Value="8"/>
       <Setter Property="DockPanel.Dock" Value="Top"/>
      </Style>
      <Style TargetType="ComboBox">
       <Setter Property="Width" Value="150"/>
       <Setter Property="Margin" Value="8"/>
       <Setter Property="DockPanel.Dock" Value="Top"/>
      </Style>
     </Window.Resources>
    
     <Border Margin="10" BorderBrush="Silver" BorderThickness="3" Padding="8">
      <DockPanel>
       <TextBlock>Choose a Color:</TextBlock>
       <ComboBox Name="myComboBox" SelectedIndex="0">
        <ComboBoxItem>Green</ComboBoxItem>
        <ComboBoxItem>Blue</ComboBoxItem>
        <ComboBoxItem>Red</ComboBoxItem>
       </ComboBox>
       <Canvas>
        <Canvas.Background>
         <Binding ElementName="myComboBox" Path="SelectedItem.Content"/>
        </Canvas.Background>
       </Canvas>
      </DockPanel>
     </Border>
    </Window>
    

    Of course if the source and target properties has diferent types you must to convert the values with a converter

    Read this article to know more about the converters:

    http://www.c-sharpcorner.com/UploadFile/dpatra/804/

     


    Please mark posts as answers/helpful if it answers your question
    Sunday, November 28, 2010 8:51 PM
  • Yes I do get that bit, but what if the controls are on 2 seperate user controls, how do I bind the controls from the 2 Usercontrols?

    Cheers Mike

    Monday, November 29, 2010 5:36 PM
  • <StackPanel Orientation="Horizontal">
    <local:UCOne x:Name="UCOne" Width="200"></local:UCOne>
    <local:UCTwo Width="{Binding Path=Width, ElementName=UCOne}"></local:UCTwo>
    </StackPanel>
    
    

    Javaman
    • Proposed as answer by Andoni Arroyo Monday, November 29, 2010 7:04 PM
    Monday, November 29, 2010 5:56 PM
  • Sorry, no matter what I try this is just not working or I am missing something really fundemental.

     

    TIA Mike

    <Grid>
    
    <local:UC2 x:Name="rulers" MinimumTickSpacing="40" Visibility="Hidden" Linethickness="2" />
    
    <local:UC1 x:Name="rulerspanel" Width="220" OpacityValue="{Binding Path=Opacity, ElementName=rulers}" Height="200" HorizontalAlignment="Right" VerticalAlignment="Bottom"></local:UC1>
    
    <Grid/ >
    
    
    So UC1 has a slider in it and I have done a dependency property Opacityvalue, I am trying to figure out how i get the slider to change the opacity of the UC2, I have tried so many binding ways I am going mad. please help.
    Monday, November 29, 2010 10:06 PM
  • I think your problem is that you are binding the Opacity of rulers to the Opactiy of rulerspanel.  I imagine that you want the value of the slider rather than the opactiy.

    Lloyd Sheen


    Lloyd Sheen
    Monday, November 29, 2010 10:11 PM
  • Thats what I want yes, the value of the slider in UC1 to affect the opacity of the ruler in UC2

    <Grid >
        
        <local:UCRulersOverlayPanel x:Name="rulerspanel" Width="220" Height="200" HorizontalAlignment="Right" VerticalAlignment="Bottom"></local:UCRulersOverlayPanel>
        
        <local:RulersOverlay x:Name="rulers"  MinimumTickSpacing="40" Visibility="Visible" Linethickness="2" />
    
    <Grid/>

    So how do I bind these two controls to have the value property of the slider in UC1 affect the opacity of the UC2 control.

    TIA Mike

    Monday, November 29, 2010 10:23 PM
  • You can't do this directly because that would break the encapsulation of the UserControls.   The fact that UC1 contains a slider is an implementation detail that should be hidden from the world outside UC1 - in particular it's hidden from UC2.   Similarly, the ruler inside UC2 is hidden from UC1.

    But you can do it indirectly by defining a new depencency property on UC1, let's call it MyValue.  Inside UC1, bind the slider's Value to the UC's MyValue property.   Similarly, define a new property MyOpacity on UC2, and bind the ruler's Opacity to the UC's MyOpacity.    Now you can bind these new properties to get the effect you want:

    <UC2 x:Name="UC2"/>
    <UC1 x:Name="UC1"  MyValue="{Binding ElementName=UC2, Path=MyOpacity}"/>

    • Marked as answer by surfluds Tuesday, November 30, 2010 3:40 PM
    Tuesday, November 30, 2010 5:55 AM
  • Yes, Sam is exactly correct on this:

    In your code behind of the UC with the Slider you have to do something similar to this.

    using System;
    using System.Windows.Controls;
    using System.ComponentModel;
    
    namespace TestListBox
    {
      /// <summary>
      /// Interaction logic for UCOne.xaml
      /// </summary>
      public partial class UCOne : UserControl, INotifyPropertyChanged
      {
        public UCOne()
        {
          InitializeComponent();
          _SliderValue = 1;
        }
        private double _SliderValue;
        public double SliderValue
        {
          get { return _SliderValue;}
          set { _SliderValue = value;
             PropChanged("SliderValue");      }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void PropChanged(String PropName) {
          if (PropertyChanged != null) { 
            PropertyChanged(this, new PropertyChangedEventArgs(PropName));
          }
        }
      }
    }
    

    Then in the mark up on the main window where UC1 and UC2 are located you need to "Bind" to this property.

    <local:UCOne x:Name="UCOne" Width="200"></local:UCOne>
    <local:UCTwo Width="200" 
     Opacity="{Binding ElementName=UCOne, Path=SliderValue}"> 
    

    This will solve the problem...

    The key to remember is that any property needing to be exposed is only seen by WPF when a DependencyProperty is exposed.  In this case we used the INotifyProperty changed event and the appropriate Getter/Setter methods to expose these properties.  WPF then will see the Getter/Setters which allows you to see them in the XAML Designer by clicking on the opacticy property and choosing the appropriate Bindings.  This is confusing to a person just learning, but over time it becomes crystal clear.


    Javaman
    • Marked as answer by surfluds Tuesday, November 30, 2010 3:40 PM
    Tuesday, November 30, 2010 2:26 PM
  •  Your replies were spot on and after looking at your code I was able to get all functionality working, In the mean time though I have found a not more effective but easier/quicker route and that was by binding the datacontext of one control to the other and this worked like a charm, no DPs needed or events. If you are interested in an exemple of this xaml I shall reply if asked.

    Cheers again all Mike

    Unless there is a downside I have not come across with the datacontext route...

    Tuesday, November 30, 2010 3:41 PM
  • Would be interested in alternate solution as I tried to do that in the XAML designer without success.
    Javaman
    Tuesday, November 30, 2010 11:07 PM