snowy hedgehog
snowy hedgehog

Reputation: 672

DataTemplateSelector and its weird behavior

I am using a DataTemplateSelector inside a ContentControl. I have 3 different DataTemplates based on 3 different object types. When I set the content of my ContentControl to data of the mentioned types, the DataTemplateSelector swaps to the specific DataTemplate AND the selector futhermore seems to rollback/reset the values from the old template. Why is that so?

Edit: I figured out that the values get resetted because I have an attached property caled Prop and inside its OnPropertyChangedCallback it notifies me about the Prop having value null on swapping between DataTemplates. You can see that attached property in code below.

Can somebody help me out what happens behind this swapping mechanism of DataTemplateSelector?

Here is a deeper explaination with code:

public void Window1()
{
  InitalizeComponents();
}

public void OnClick(object sender, RoutedEventArgs e)
{
  if(this.DataContext == null)
    this.DataContext = "Hallo";
 else{
  if(this.DataContext is string)
    this.DataContext = 123;
  else{
   if(this.DataContext is int)
     this.DataContext = null;
   }
  }
}
By clicking on Button few times I change the type and so in ContentControl the selector changes to DataTemplate.

The selector looks like this below. It swaps between textDataTemplate and numericDataTemplate and one more template. As I mentioned i have those three type which are string, int, and one more, that i wish not to metion. Their DataTemplates are called textDataTemplate, numericDataTemplate and that one more. :)

<local:MyTemplateSelector x:Key="dataTemplateSelector"
                                 TextTemplate="{StaticResource textDataTemplate}"
                                 NumericTemplate="{StaticResource numericDataTemplate}"/>

public class MyTemplateSelector : DataTemplateSelector
{
  public DataTemplate TextTemplate;

  public DataTemplate NumericTemplate;


  public DataTemplate Select(object item, Culture.....)
  {
    if(item is string)
    {
      return this.TextTemplate;
    }
    else
    {
      return this.NumericTemplate;
    }
  }
}

ContentControl and XAML looks like this:

<Button Click="OnClick" Content="Click Me"/>


<ContentControl Name="contentCtrl"
                Content="{Binding}"
                Width="123"
                ContentTemplateSelector="{StaticResource dataTemplateSelector}" />

And this is how textDataTemplate looks alike.

<DataTemplate x:Key="textDataTemplate">
    <TextBox  x:Name="text" my:AttProperties.Prop="{extension:MarkupExt value}" Text="{Binding Path=Txt, Mode=Default, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>

numericDataTemplate looks similar to textDataTemplate just that only digits are allowed.

The Prop is my attached property from AttProperties class of type string. The Prop is somewhere inside of all three DataTemplate. Above the Prop is sitting on a TextBox but it could be a Label too. The markupextension is just a "return Hello". The extension is just there to test how to create a custom markupextension. There is no big deal with the extension. It shouldnt have to do much with the swapping of DataTemplates.

One more time to explain my problem. Swapping seems reselts/rollback my old templates. I swap from textDataTemplate to lets say numericDataTemplate and the Prop of textDataTemplate gets set to null but the value before was "Hello".

Why is that happening? It seems like the same behavior with using tiggers. Once a Trigger is no more valid it rollsback the used values. Is a DataTemplateSelector using some kind of same mechanism as Triggers?

Edited: The attached property is just a simple .RegisterAttached with an OnPropertyChangedCallback. Inside OnPropertyChangedCallback I figured the prop is null when swapping the dataTemplates.

Upvotes: 1

Views: 2440

Answers (1)

notacat
notacat

Reputation: 691

If you use two-way binding in numeric template and it only accepts something like Double, it can set value to number. But no one can be sure without seeing full code. It's possible that your own code does something wrong. To understand things better, create your own control, derived from the ContentControl, and use it in your sample. Then override control methods OnContentxxxChanged, insert breakpoints there and debug your application. You should understand, what's going on with your data and with template selector. When application stops on breakpoint, carefully check all values and look at stack trace. To debug bindings you can insert IValueConverters, it would give you place in code, where you can check values.

I really suggest you to make the simplest working thing first, and then go to more complicated things such as textboxes with two-way bindings to some property of some control which you didn't show in your question. Here is a working version with TextBlocks:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    public void OnClick(object sender, RoutedEventArgs e)
    {
        if (this.DataContext == null)
            this.DataContext = "Hallo";

        else if (this.DataContext is string)
            this.DataContext = 123;

        else if (this.DataContext is int)
            this.DataContext = null;
    }
}
public class MyTemplateSelector : DataTemplateSelector
{
    public DataTemplate TextTemplate {get; set;}

    public DataTemplate NumericTemplate {get; set;}

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is string)
        {
            return this.TextTemplate;
        }
        else
        {
            return this.NumericTemplate;
        }
    }
}

and xaml:

    <Grid>
    <Grid.Resources>
        <DataTemplate x:Key="numericDataTemplate">
            <TextBlock Foreground="Red" Text="{Binding}" />
        </DataTemplate>
        <DataTemplate x:Key="textDataTemplate">
            <TextBlock Foreground="Green" Text="{Binding}"/>
        </DataTemplate>
        <local:MyTemplateSelector x:Key="dataTemplateSelector"
                             TextTemplate="{StaticResource textDataTemplate}"
                             NumericTemplate="{StaticResource numericDataTemplate}"/>
    </Grid.Resources>
    <StackPanel>
        <Button Click="OnClick" Content="Click Me" VerticalAlignment="Top"/>
        <ContentControl Name="contentCtrl"
                Content="{Binding}" 
                Width="300" Height="100" 
                ContentTemplateSelector="{StaticResource dataTemplateSelector}" />
        </StackPanel>
</Grid>

Compare with your code. When you inherit from DataTemplateSelector, you should override SelectTemplate method and don't invent methods with other names. All controls such as ContentControl will only use SelectTemplate. Etc..

Obviously, all works and DataTemplateSelector does nothing wrong. I suppose, your problem is somewhere in your data and bindings

And look at your OnClick method - it always sets DataContext to null

Upvotes: 3

Related Questions