locked
WPF Data Binding Issue RRS feed

  • Question

  • If your DataContext is setup correctly so that the txtFirstName works why doesn't ccFirstName work? What makes this even more interesting is that the binding is there on the ccFirstName but it is acting like a OneWay binding instead of a TwoWay binding. That is updating the value in txtFirstName will change the value of ccFirstName but updating the value in ccFirstName doesn't update the value in txtFirstName. A Breakpoint on the FirstName property set in the DataContex will only break on txtFirstName.
    <TextBox Name="txtFirstName" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
    <ContentControl Name="ccFirstName" Content="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
    <ContentControl.ContentTemplate>
    <DataTemplate>
    <TextBox Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
    </DataTemplate>
    </ContentControl.ContentTemplate>
    </ContentControl>
    • Edited by Buck Smith Wednesday, March 9, 2011 2:35 PM fixed formatting
    Wednesday, March 9, 2011 2:33 PM

Answers

  • Path=. and Mode=TwoWay just don't work together. Path=. means you bind to an object, but you cannot replace the object itself. It's like when you open a file to access it's content - you can replace it's content but you cannot replace the file. The converter's ConvertBack is not called in such situation.
    • Marked as answer by Min Zhu Monday, April 11, 2011 2:34 AM
    Wednesday, March 9, 2011 10:32 PM
  • Hi Buck,

    One way to resolve this problem is to expose a Binding type property on your custom control and use that to deliver the binding to the TextBox in the code behind.

    Here is a simple sample.

     

      private void Loaded(object sender, RoutedEventArgs e)
      {
       TextBox textBox = new TextBox();
    
       BindingOperations.SetBinding(textBox, TextBox.TextProperty, Binding);
    
       this.Content = textBox;
      }
    
      public BindingBase Binding { get; set; }
    

     

     

     

    <local:MyCustomControl Binding={Binding Path=FirstName}/>
    

     

    If you want to use different sets of controls to template the data, you can either manually check the type of the target property in the code behind, or you can expose another property which indicates the type of the data ("Text","Date",etc...). Another option is to create different custom controls for each type.

    DataGrid's columns is a very good example of what you want to accomplish. You can see its source code here.

    Hope this helps.

     


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Min Zhu Monday, April 11, 2011 2:33 AM
    Thursday, March 10, 2011 3:44 AM

All replies

  • Hi,

    try out this ,it will work

     <TextBox Name="txtFirstName" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" DockPanel.Dock="Top" />
        <ContentControl Name="ccFirstName" DockPanel.Dock="Top">
          <ContentControl.ContentTemplate>
            <DataTemplate>
              <TextBox Text="{Binding Path=DataContext.FirstName,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContentControl}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
            </DataTemplate>
          </ContentControl.ContentTemplate>
        </ContentControl>

    as u r using DataTemplate, u have to use  RelativeResource to find out the real datacontext

    hope this helps u

    Wednesday, March 9, 2011 2:58 PM
  • Hello,

    try this:

     

    <TextBox Name="txtFirstName" Width="200" Height="22" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
                <ContentControl Name="ccFirstName" Content="{Binding FirstName,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                <ContentControl.ContentTemplate>
                    <DataTemplate>
                            <TextBox  Width="200" Height="22" Text="{Binding Content,ElementName=ccFirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
                    </DataTemplate>
                </ContentControl.ContentTemplate>
            </ContentControl>

     

    Good Luck

     

     

    Wednesday, March 9, 2011 3:06 PM
  • What about this scenario:
    FirstName, LastName are string
    IsActive is bool
    ActiveDate is DateTime

    I can't just inject FirstName at the DataTemplate level.  I need to inherit the bindings of the ContentControl.

    <local:MyTemplateSelector x:Key="myTemplateSelector"/>
    <DataTemplate x:Key="StringTemplate">
     <TextBox Text={Binding Path=.}/>
    </DataTemplate>
    <DataTemplate x:Key="BoolTemplate">
     <CheckBox IsChecked={Binding Path=.}/>
    </DataTemplate>
    <DataTemplate x:Key="DateTimeTemplate">
     <DatePicker SelectedDate={Binding Path=.}/>
    </DataTemplate>

    <ContentControl Content={Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}
      ContentTemplateSelector={StaticResource myTemplateSelector}/>
    <ContentControl Content={Binding Path=LastName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}
      ContentTemplateSelector={StaticResource myTemplateSelector}/>
    <ContentControl Content={Binding Path=IsActive, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}
      ContentTemplateSelector={StaticResource myTemplateSelector}/>

    <ContentControl Content={Binding Path=ActiveDate, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}
      ContentTemplateSelector={StaticResource myTemplateSelector}/>

    public class MyTemplateSelector:DataTemplateSelector{
     public override DataTemplate SelectTemplate(object item, DependencyObject container){
      FrameworkElement element = container as FrameworkElement;
      if(item != null && element != null){
       var type = item.GetType();
       if(type == typeof(string))
        return element.TryFindResource("StringTemplate") as DataTemplate;
       else if(type == typeof(bool))
        return element.TryFindResource("BoolTemplate") as DataTemplate;
       else if(type == typeof(DateTime))
        return element.TryFindResource("DateTimeTemplate") as DataTemplate;
      }  
     }
    }

     

    Wednesday, March 9, 2011 5:20 PM
  • Try to use RelativeSource as per my suggestion if u want to do Binding in DataTemplate..
    Wednesday, March 9, 2011 5:26 PM
  • Does not work because the datatemplate will have no knowledge of DataContext.FirstName. The block would need to be something similar to this:

    <TextBox Text="{Binding Path=., RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContentControl}}}" />

    Which is why I was using:
    <TextBox Text="{Binding Path=.}" />
    or
    <TextBox Text="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}}" />

    My problem isn't that the binding is not there.  My problem is that the binding is behaving as OneWay instead of TwoWay.  Even if I specify TwoWay.

    Wednesday, March 9, 2011 6:54 PM
  • Why are you setting the Content of the ContentControl to the first name?  You should bind the Content to the object {Binding } and use the ContentTemplate to display the name {Binding FirstName}

    This assumes your DataContext of the ContentControl will be an object with a FirstName property.

    <ContentControl Name="ccFirstName" Content="{Binding }">
     <ContentControl.ContentTemplate>
     <DataTemplate>
      <TextBox Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
     </DataTemplate>
     </ContentControl.ContentTemplate>
    </ContentControl>
    
    Wednesday, March 9, 2011 8:28 PM
  • Path=. and Mode=TwoWay just don't work together. Path=. means you bind to an object, but you cannot replace the object itself. It's like when you open a file to access it's content - you can replace it's content but you cannot replace the file. The converter's ConvertBack is not called in such situation.
    • Marked as answer by Min Zhu Monday, April 11, 2011 2:34 AM
    Wednesday, March 9, 2011 10:32 PM
  • This is an example to illustrate my issue.  Ideally this would be hidden behind a custom control.  You would not have access to the DataTemplates and you would not know that you needed to bind to FirstName at the DataTemplate level.  All you know is that the ContentControl is binding on FirstName and that the TextBox needs to bind to whatever ContentControl.Content is binding to.  Look at the example where the DataTemplateSelector is being used.

    <local:MyCustomControl Content={Binding Path=FirstName}/>

    <local:MyCustomControl Content={Binding Path=BirthDate}/>

    Thursday, March 10, 2011 12:24 AM
  • This explains the problem but not the answer.  How can I tell the TextBox to inherit binding in a generic way?
    Thursday, March 10, 2011 12:26 AM
  • Hi Buck,

    One way to resolve this problem is to expose a Binding type property on your custom control and use that to deliver the binding to the TextBox in the code behind.

    Here is a simple sample.

     

      private void Loaded(object sender, RoutedEventArgs e)
      {
       TextBox textBox = new TextBox();
    
       BindingOperations.SetBinding(textBox, TextBox.TextProperty, Binding);
    
       this.Content = textBox;
      }
    
      public BindingBase Binding { get; set; }
    

     

     

     

    <local:MyCustomControl Binding={Binding Path=FirstName}/>
    

     

    If you want to use different sets of controls to template the data, you can either manually check the type of the target property in the code behind, or you can expose another property which indicates the type of the data ("Text","Date",etc...). Another option is to create different custom controls for each type.

    DataGrid's columns is a very good example of what you want to accomplish. You can see its source code here.

    Hope this helps.

     


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Min Zhu Monday, April 11, 2011 2:33 AM
    Thursday, March 10, 2011 3:44 AM
  • Hi Buck,

    Just checking in to see if the information was helpful. Please let us know if you would like further assistance.

    Have a great day!


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, March 14, 2011 2:36 AM
  • I am not sure if this will work.  I have been reading through the code and marking the information that I need.  It may take me a few weeks to test this approach.
    Monday, March 14, 2011 12:50 PM
  • Hi Buck,

    Thanks for the update. If you have any further questions on this, feel free to let us know.

    Best regards.


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, March 15, 2011 1:54 AM
  • Hi Buck,

    Any update of this problem?

    I mark this issue as "Answered" for now. If you have any new questions or concerns about this issue, please feel free to let me know.

    Thank you and have a nice day!


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, April 11, 2011 2:33 AM