Silverlight DataBinding, avoid BindingExpression Path error with missing properties, hide controls instead

imagine the following simple Models (an example for simplicity, we actually have MVVM, but that doesn't matter):

public class User { public string Username { get; set; } } public class StackOverflowUser : User { public int Reputation { get; set; } } 

Now we have a Silverlight UserControl that contains the following Controls (again, this is just an example, split up to the kernel):

 <Grid> <TextBlock Text="Username:" /> <TextBlock Text="{Binding Path=Username}" /> <TextBlock Text="Reputation:" /> <TextBlock Text="{Binding Path=Reputation}" /> </Grid> 

Now I would like this UserControl be compatible with both models, User and StackOverflowUser . I can set the UserControl DataContext either User or StackOverflowUser Type:

 this.DataContext = new User { Username = "john.doe" }; 

If set to StackOverflowUser , everything works fine. If set to User , I get the "BindingExpression Path" error because the Reputation property is not in the User model. Which I fully understand.

Is there a way 1) to avoid this exception and 2) to control the visibility of controls, collapse when the associated property is unavailable?

Of course, we prefer an elegant solution in which the problem is solved by setting up the Binding Expression expression and / or using converters, etc. and if possible, avoid tons of code.

Thanks in advance for your help and suggestions,
best wishes,

Thomas

+4
source share
4 answers

I finally solved the problem. The employee finally implemented the solution, including a workaround for the DataTemplates WPF DataType attributes (or, in general, the DataTemplateSelector). It's not very pretty (I think there is no workaround), but it works. Unfortunately, I cannot post code snippets due to its sealed source. But after that I found some links, providing a pretty similar solution like this one: Silverlight: port DataTemplateSelector . If you have a similar problem, this will also help you. Here or there are more thoughts on this.

The actual solution follows Ozan’s prompts. Unfortunately, his solution does not work, so I do not want to mark his comment as an accepted answer, but I give at least the upper part.

Thanks!

Best wishes,

Thomas

0
source

Unfortunately, Silverlight is limited in its polymorphic behavior regarding DataTemplates, I can only think of a workaround:

Give the User class the Reputation property too, but make it pointless, like -1. Then apply the style to the TextBlocks reputation:

  <Page.Resources> <Style Key="Reputation"> <Style.Triggers> <DataTrigger Binding="{Binding Path=Reputation} Value="-1"> <Setter Property="Visibility" Value="Invisible" /> </DataTrigger> </Style.Triggers> </Style> </Page.Resources> ... <TextBlock Text="Reputation:" Style="{StaticResource Reputation}"> <TextBlock Text="{Binding Path=Reputation}" Style="{StaticResource Reputation}"> 

You can also try (I cannot verify this):

  • assignment to the class User a new property identifying its type,
  • make a second style for a second TextBlock
  • bind its DataTrigger to the type identification property and move the {Binding Path = Reputation} declaration to Setter:

     <Style Key="ReputationContent"> <Style.Triggers> <DataTrigger Binding="{Binding Path=Type} Value="StackOverflow"> <Setter Property="Visibility" Value="Invisible" /> <Setter Property="Text" Value="{Binding Path=Reputation}" /> </DataTrigger> </Style.Triggers> </Style> 

But you see that there is no elegant way, it is a shame that the DataTemplate does not have the DataType property in Silverlight.

+1
source

You mentioned that you are using MVVM. This value of your viewing model is to generate model data in preparation for viewing. The view model can have available properties for both the username and reputation (and, possibly, another bool for binding visibility). The view model will include all the logic on how to populate these properties from any model (User or StackOverflowUser). The view will not know the User object or StackOverflowUser, only the viewmodel.

+1
source

I know this has already been answered, but I still think it's worth this post. Using reflection, you can have a property in your ViewModel that will easily handle Dto objects that sometimes have a property. Reflection can be expensive, so weigh it with your decision.

 public int? Reputation { get { var prop = Dto.GetType().GetProperty("Reputation"); return (prop != null)? (int)prop.GetValue(Dto, null) : null; } set { var prop = Dto.GetType().GetProperty("Reputation"); if(prop !=null) prop.SetValue(Dto,value, null); } } 
0
source

Source: https://habr.com/ru/post/1316555/


All Articles