Default Value for Control Template binding in WPF MVVM - c#

Probably a dumb question but to make a default value for binding in a WPF ControlTemplate what is the best way in terms of performance?
I've tried several option:
Priority binding: It didn't work, probably my bad...
ControlTemplate Trigger: Works well, I use it on a property which always return true in my case:
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="TextBoxWidth" Value="300" />
</Trigger>
</ControlTemplate.Triggers>
FallBackValue: Works well too.
Is it better to work with trigger, FallBackValue or is there another way to do it?
Thanks in advance
----------------- UPDATE ----------------
After doing more research it seems that the best way to achieve my goal is simply using the styles...
I don't know why it didn't work in the first place but just doing this seems to work:
<Style TargetType="local:EditableWidthText">
<Setter Property="TextBoxWidth" Value="300"/>
</Style>
No need for FallbackValue anymore. It works when there is a binding, when there is no binding and when it's overrided by another style. Exactly what I need.
Thanks Sinatr for your comment about performance of FallbackValue.
Fyi Priority Binding:
PriorityBinding lets you associate a binding target (target) property with a list of bindings.
The first binding that returns a value successfully becomes the active binding.
https://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding%28v=vs.110%29.aspx
I never used template selector nor datatemplate, but I think it's a bit overkill for my need, no?
----------------- UPDATE #2----------------
#Sinatr:
I've a Mainviewmodel which contains several ViewModel binded to several usercontrols displayed into the MainView.
(Something like one of my other post: Navigate through UserControl with MVVM LIGHT (WPF))
But this controlTemplate is for a looklessControl (something quite simple, juste a label and a textbox).
In this LookLess Control I've created 4 dependency properties.
In the control template I've binded 2 of them to the Text/Content propertie of the Textbox/Label
and two to their width. (I want the user to be able to create forms in the future, binding way).
For example the label looks like this in the ControlTemplate:
<Label
Width="{Binding Path=LabelWidth,
RelativeSource={RelativeSourceAncestorType=local:EditableWidthText}}"
Content="{Binding Path=LabelText,
RelativeSource={RelativeSource AncestorType=local:EditableWidthText}}"/>
I just want the width to be equals to 130 when there is no binding defined.
But I don't want problem of performance because of a binding failed or something like this.
Template selector are very interesting, but I don't think that applied in my case.
There is no switching within the datas, it's just a default value. So I don't think that apply either.
(But maybe I'm wrong :p )
So maybe I think that styles are the way to go?

Related

Ignore global style for specific control and it's children

I've done my best to ensure this isn't an exact duplicate of other questions and have tried quite a few possible solutions. Maybe I'm just not doing the right searches.
The problem
I have a resource dictionary with a bunch of default styles. For example, most control types have a default height of 26 to provide some consistency in my layout.
So for example, I have the following style for TextBlock
<Style TargetType="TextBlock">
<Setter Property="Height" Value="26"/>
</Style>
The problem I have is that the Telerik RadGridView uses TextBlock controls to display the column header text and these controls are adopting the default style and end up with a height of 26.
I would like to get the RadGridView, and all of it's child controls, to ignore the styles in my resource dictionary.
I tried this but it didn't work
I found quite a few suggestions to set the style to Null for controls that you want to ignore global styles. I tried the following but it didn't work as it doesn't seem to apply to the child controls inside of the RadGridView.
<telerik:RadGridView Style="{x:Null}">
...
</telerik:RadGridView>
This works but may not be the best solution
I found the following question which had a solution I was able modify and use
Setting style based on existence of an ancestor type
Using the answers in that question I created a converter to check if a control has an ancestor of a specific type. The code is pretty much the same as in the above question so to keep this question from getting too long I won't paste it here.
I modified my style to this
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource HasAncestorTypeConverter},
ConverterParameter={x:Type telerik:RadGridView}}"
Value="False">
<Setter Property="Height" Value="26"/>
</DataTrigger>
</Style.Triggers>
</Style>
Although this works, I was concerned about performance as my application grows and there's more and more controls. I have similar triggers for many control types that could be used in the RadGridView.
Every control is going to be recursively checking if it has a RadGridView as an ancestor. Most won't, so they need to search all the way up to their base container before knowing they don't have a RadGridView as an ancestor.
My question
So after that the lead up, my questions is whether there's a better way to do this? Is there something I can wrap the RadGridView with that will tell it, and it's child elements, to ignore the styles in my resource dictionary?
Yes there is better way to do this: define empty style either in Resources of RadGridView itself, or if you want to apply that to all RadGridViews - define it in resources of RadGridView style itself, like this:
<Style TargetType="telerik:RadGridView">
<Style.Resources>
<Style TargetType="TextBlock" />
</Style.Resources>
</Style>
That will prevent all children of RadGridView to inherit global TextBlock style (instead they will use "default" TextBlock style)

How do you edit setters in a resource dictionary

Ok I would think this would be fairly simple, but it seems almost impossible to do. I have a resource dictionary in my app and I define a style like so:
<Style x:Key="appBackground" TargetType="Grid">
<Setter Property="Background" Value="#ebf2f3"/>
</Style>
My end goal would be to allow users to select a color and let them change the appearance of the app. I would like to still use my resourcedictionary to control the styles, so they can pick like background color, main text color, and accent color and remain consistent throughout the app. But I cannot find a way to edit the resourceDictionary. I have tried to use the System.Windows.Markup.XamlWriter.Save method which was talked about in this SO post (How to dynamically add key and value to the ResourceDictionary in wpf?) but when I do it adds:
<s:String x:Key="appBackground">Blue</s:String>
Any ideas or suggestions out there??
You can make value of your setter a binding to static resource, that you would be dynamically changing like in the answer you referenced.
<Color x:Key="myAppBackground">#ebf2f3</Color>
<Style x:Key="appBackground" TargetType="Grid">
<Setter Property="Background" Value="{StaticResource myAppBackground}"/>
</Style>
After this you would be just changing value of myAppBackground.
Another suggestion, though it might not solve your problem since it's a bit different. You still make values of setters a binding, but instead of dynamically changing the values you would have 2 ResouceDictionaries with same keys, but different values. You would merge the one you want with the rest in your application and apply that. I used this approach to load different color themes on application startup, but if you need to do this many times it might be slow and not efficient approach.

Hiding TreeView items

I've been trying to just hide items from a TreeView. I'm using a custom data type as source (called SettingsMenuItem) which inherits from FrameworkElement (currently FrameworkContentElement, because otherwise the TreeView renders them wrong).
My goal is by setting the VisibilityProperty of these FrameworkElements to either Collapsed or Visible that I'm able to hide certain items (including their children). I know that this can be done by deleting items from the source collection. But that's not what I want. It would mean that I have to mirror each collection in order to keep track of it's actual items, bind to each one in order to be notified about Visibility-changes and create a new collection each time one changes. A lot of overhead for this.
Right now I have no clue how I could accomplish that. I figure it's related to the ItemsGenerator, but I haven't seen any possibility to override it's behaviour. I thought TreeView would be able to detect Visibility, but obviously it doesn't. As alternative I thought of a custom TreeViewItem (maybe even TreeView if necessary) - but at this point the abstraction of this whole system overwhelms me. I don't know where to start and what is actually necessary to solve the problem.
Tips what I have to change or implement by myself would be more than enough. A complete solution would be nice.
You can do this using a data trigger bound to a property (e.g. "IsVisible") in you tree data nodes:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
While this technically answers your question I would be wary of actually doing it. User3690202's comment is correct, it's the sort of thing you would normally do via filtering in your view model.
For alternate solution using code behind xaml.cs:
To Remove a specific TreeViewItem from a TreeView which is created from a code behind.
TreeViewItem treeViewItem1 = new TreeViewItem
{
Visibility = Visibility.Collapsed,
};
use the code with TreeViewItem you want to hide in a if condition to hide specific TreeViewItem Header let say "Cars" and you want to hide it and use the code with if condition to hide "Cars" TreeViewItem.

XAML - ListViewItem - IsEnabled Binding

Please put me out of my misery:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsEnabled" Value="{Binding MyBoolField}" />
</Style>
</ListView.ItemContainerStyle>
Where MyBoolField is a property available on each item in the ListView's assigned ItemsSource, and is of course of type bool.
The desired behaviour is that the IsEnabled property of each ListViewItem is bound to the MyBoolField property of the object it contains (an element from the ListView's ItemsSource). The example above however pays no attention to this binding and is always true.
If I set the value to false statically, the item becomes disabled as expected.
I can only assume this is an issue of scoping, or a restriction on the use of bindings in this particular scenario, however I am unable to find any documentation to support this.
Perhaps it is useful to know that bindings set up in the DataTemplate assigned to this ListView's ItemTemplate all work okay, so the problem is hopefully not that fundamental/stupid.
Points from the Comments
There is no relevant output in the debug 'Output' window.
Binding the MyBoolField property elsewhere works fine, as such the underlying data source is providing a Boolean value correctly and this seems to be solely an issue of binding it in this context.
Ok, reading this post it seems that its a known limitation of WinRT, maybe you find something useful there.
One thing i would also try, because the OP in that post said something regarding that is. Using a Self binding, and use Path=DataContext.MyBoolField
Further reading, this is a limitation from Silverlight, which WinRT was build upon. So this is a workaround which should also work for WinRT

Beyond Data Validation Styling: Styling based on other properties

I hope that this question has not been asked elsewhere, but I'm having difficulty finding the right search terms to bring up any already-existing answers to this problem.
I'm implementing a program in C# WPF (MVVM) with many interview-style screens where the user inputs scientific data in an orderly manner. We would like to have the Textbox's and DataGrid's change Foreground and Background colors on an individual basis based on whether the data in that control has been inputted by the user, inputted as a default value by the program, or is a template value from another file the user imported. On top of this, we would like the UI to respond to validation checks from IDataErrorInfo implemented in the ViewModel.
Thus, the data displayed in a TextBox could be blue if it is a templated value, green if a program default, black if user inputed, and red if IDataErrorInfo says it is bad data.
My initial answer for implementing this was to create a custom class:
class AdornerString{
private string _myString;
private bool _isTemplate;
private bool _isDefault;
public string MyString{
get{
etc.
}
set{
etc.
}
}
// accessor properties and Constructors omitted for brevity
}
Then I have all my TextBox.Text properties in the View bound like so:
<TextBox Text="{Binding Path=someAdornerString.MyString,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
and apply a style with DataTriggers that responds to someAdornerString's properties to create the appropriate colors.
However, IDataErrorInfo on the ViewModel doesn't validate the TextBox anymore. Why is this so, and is there a better way to solve my problem? I can't validate inside the AdornerString class.
A possible work-around, though undesirable:
The only other solution I can think of is to have two properties in the ViewModel for each field entered by the user- one for the data itself and the other being the same custom class as above minus the string. However, this means I can't generalize the style used for the textboxes. Each TextBox would have to have a custom style something like this:
<TextBox.Style>
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource OtherStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=**instanceCustomClass**.IsDefault}"
Value="True">
<Setter Property="Foreground"
Value="Green" />
</DataTrigger>
<Trigger Property="Validation.HasError"
Value="true">
<Setter Property="Foreground"
Value="OrangeRed" />
</Trigger>
</Style.Triggers>
</Style>
since each UI field has a specific custom class associated with it.
I would seriously rather not implement this way, as I have possibly 100+ pages of input screens, with each screen having 1-12 TextBox's each, ignoring the DataGrids thrown in the mix (with parellel arrays of data and their associated custom classes).
Any ideas? I've read about custom validation, though I yet don't see how this might help in this case.
Ignore making use of the IDataErrInfo validation all together, as it seems that the you really want to make it 1 of 4 values...and 'bad' data just happens to be one of them.
You need to keep the items on an even playing field since you are treating them the same, just differentiating colors. Use a single property with an object wrapping the value and the state of the model within the ViewModel. Ignore IDataErroInfo and then use a converter to provide the coloring and then add a delegate to the AdornerString that will be set to the validation function written in the ViewModel for it.

Categories

Resources