(wpf) Application.Current.Resources vs FindResource - c#

So, I'm making a GUI thingy with WPF in C#. It looks like this:
It's not done right now. Those 2 rows are my attempt at making a sort of data table, and they are hard-coded in the XAML.
Now, I am implementing the add new fruit button functionality in the C#.
I have the following style in the XAML that governs what the background images of the rows should look like:
<Style x:Key="stretchImage" TargetType="{x:Type Image}">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Stretch" Value="Fill"/>
</Style>
So, in the code, I create an image for each column, col0, col1, and col2, and if I use the following code,
col0.Style = (Style)Application.Current.Resources["stretchImage"];
col1.Style = (Style)Application.Current.Resources["stretchImage"];
col2.Style = (Style)Application.Current.Resources["stretchImage"];
it adds a new row that looks like this:
As you can see, it's not quite right...
And stretching the window exacerbates the problem:
It seems to not be respecting the style's "Stretch" property.
But then, if I instead change my style loading code to
col0.Style = (Style)FindResource("stretchImage");
col1.Style = (Style)FindResource("stretchImage");
col2.Style = (Style)FindResource("stretchImage");
It works beautifully:
(Again, the app isn't finished, so don't worry about that), but my main question is: What's the difference between Application.Current.Resources[] and FindResource()? Why does one seem to ignore some of the properties while the other doesn't? And how, if at all possible, might I get Application.Current.Resources[] to work properly?

Resources can be defined on almost any element in the visual tree. FrameworkElement.FindResource() will walk up the tree looking for the resource at each node, and eventually make it all the way to Application. Application.Current.Resources[] skips all of this and goes straight for the resources on the Application. You almost always want to use FindResource() so you can override styles at various points.

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.

Unexpected behavior of AlternativeRow BackColor in WPF

I'm styling my DataGrid using an style provided by Microsoft at this page. I'm not changing this style except a single line as following that enables alternative row back color.
<!--Style and template for the DataGrid.-->
<Style TargetType="{x:Type DataGrid}">
<Setter Property="AlternationCount" Value="2"/>
... and the rest of the style
I realized a very strange behavior of this style. When you scroll up/down the data grid for few times and then you see that alternative row back color is mixed! such as following image:
I expect a sequence of light-colored + dark-colored rows which is true before scrolling; but after scrolling everything is randomly mixed-up. Before applying this style I was explicitly setting alternative row color at DataGrid definition like following which I never experienced such behavior.
<DataGrid ItemsSource="{Binding Source={StaticResource itemsSource}}" AutoGenerateColumns="False" AlternatingRowBackground="#FF58C9FD" RowBackground="#00000000"/>
Does anyone have a suggestion where should I be looking for the problem ?
That's a known virtualization problem on WPF DataGrid.
Here is a workaround, but be aware that it can cause serious performance problems if your dealing with large amounts of data on that DataGrid.
There is a good explanation on the mechanics behind this behavior here.
Gabriel mentioned that this behavior is a known issue that raises when you try to create a brand-new style for data grid (or in general any item collection).
I tried creating an style based on default style as suggested by Gabriel like following:
<Style TargetType="{x:Type DataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="AlternationCount" Value="2"/>
<Setter Property="AlternatingRowBackground" Value="OrangeRed"/>
</Style>
I tried overriding all properties I need via this style, and it works fine. Maybe there is a trick/tweak missing in styles provided by MSDN (which is duplicated in most of styles found on net) that fixes this issue. We still appreciate if anyone updates us.
Meanwhile, I suggest to override default styles by your customization and avoid any brand-new style.

How to get tag of an element in c# wpf?

I am trying to build a GUI using WPF, in which I can draw some basic shapes and store them into a xml file. Shapes are designed in a xaml file, and I added tags for each of them. Now I want to get the value of their tags in my code to store as attributes in the output xml file.
For instance, I created a rectangle shape with a tag named "RectangleTag" in my xaml file like this:
<Style x:Key="stack" TargetType="Rectangle" BasedOn="{StaticResource FlowChartRectangleStyle}"/>
<Style x:Key="stack_DragThumb" TargetType="Rectangle" BasedOn="{StaticResource stack}">
<Setter Property="IsHitTestVisible" Value="true"/>
<Setter Property="Tag" Value="RectangleTag"/
</Style>
and
<Rectangle Style="{StaticResource stack}" ToolTip="stack" StrokeThickness="2">
<s:DesignerItem.DragThumbTemplate>
<ControlTemplate>
<Rectangle Style="{StaticResource stack_DragThumb}" x:Name="StackShape" Tag="RectangleTag" />
</ControlTemplate>
</s:DesignerItem.DragThumbTemplate>
</Rectangle>
Then in my code I did:
XElement myItem = new XElement("Items",
from item in designerItems
let contentXaml = XamlWriter.Save(((DesignerItem)item).Conent)
select new XElement("Item",
new XAttribute( "Tag", item.Tag.ToString())
);
Then my GUI stops responding for this line. I believe there must be some way to get the tag here but not in this manner obviously. How can I do that? It won't necessarily be the tag, but also the x:Name or x:Key, that are enough to let me differentiate the given shapes.
I also tried this line:
new XAttribute("Tag", item.Name)
But this gives out an empty string, not the name that is assigned in the xaml file. Could someone help? Thanks.
As Sheridan stated, you are attacking this problem from the wrong direction.
First of all - required reading if you haven't yet: Model-View-ViewModel Explained
You should create a set of Model objects that define your shapes, a set of ViewModel objects that expose them to the View and define their behavior, and a View which binds to the ViewModel.
A key difference in doing it this way is that now your logic for persisting to XML is not dependent on the UI at all, so you won't have to try to use something like Tag to pass around 'magic values'.
And, as an aside, I have found that the vast majority I've relied on using Tag for anything, that has been an indicator that I'm Doing It Wrong. :)
Here is a example: assuming you have UI element is XAML (Button named _btn) with Tag property set to some value, then in any event handle (e.g. Click) associated with that element in code behind you can get the Tag value as follows:
_btn.Click+=(s,e,)=>{ string _tag = (s as Button).Tag.ToString(); };
You can apply the same logic to you case. Rgds,

Categories

Resources