Set WPF VisualStyle in C# - c#

I'm trying to indent items on a WPF navBar. I can do this in XAML using resources and setting the visualstyle to the appropriate style
<dx:DXWindow.Resources>
<Style x:Key="nestedNavBarItemL1" TargetType="ButtonBase">
<Setter Property="Margin" Value="20,0,0,0"/>
</Style>
<Style x:Key="nestedNavBarItemL2" TargetType="ButtonBase">
<Setter Property="Margin" Value="40,0,0,0"/>
</Style>
</dx:DXWindow.Resources>
.
.
<dxnb:NavBarItem Content="Source Group"
ImageSource="Images/Icons/Group.png"
VisualStyle="{StaticResource nestedNavBarItemL1}"/>
.
.
However, this is fine if the controls are already created but I'm building up the controls through code using something similar to
NavBarGroup group1 = new NavBarGroup();
group1.Header = eventItems[i].name;
group1.Tag = eventItems[i].id;
for (int i = 0; i < nodeCount; i++)
{
NavBarItem item = new NavBarItem();
item.Content = "Home";
item.Tag = "Level" + i;
//item.VisualStyle = ? How do I set VisualStyle="{StaticResource nestedNavBarItemL1}"/>
group1.Items.Add(item);
}
group1.IsExpanded = false;
navBarControl.Groups.Add(group1);
How do I set VisualStyle="{StaticResource nestedNavBarItemL1}" in code?
Thank you,
O

If you already have style defined in resources, you could use FindResource method to get it and then assign to the relevant property like this (window is where resource defined):
Style visualStyle = (Style)window.FindResource("nestedNavBarItemL1");
item.VisualStyle = visualStyle;
If you don't you can create it as shown in this codeproject article.

Define the style in app.xaml then you can access it with Application.Current.Resources["YourDefinedStyle"] as Style within your application.
ex:
item.VisualStyle = Resources["YourStyle"] as Style

Related

How to add a Style to UserControl Resources in C# programmatically?

I'm trying to do this XAML:
<UserControl.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</UserControl.Resources>
in C# code.
Here's my attempt in the UserControl Constructor:
InitializeComponent();
string labelForegroundColor = "Blue";
string labelXAMLStyle = #"<Style xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' TargetType=""Label"">
<Setter Property=""Foreground"" Value=""{LabelForegroundColor}""/>
</Style>";
labelXAMLStyle = labelXAMLStyle.Replace("{LabelForegroundColor}", labelForegroundColor);
StringReader mainLabelStyleXAMLStringReader = new StringReader(labelXAMLStyle);
XmlReader mainLabelStyleXAMLXMLReader = XmlReader.Create(mainLabelStyleXAMLStringReader);
Style mainLabelStyle = (Style)XamlReader.Load(mainLabelStyleXAMLXMLReader);
this.Resources.Add("LabelStyle", mainLabelStyle);
When I have the XAML in my UserControl it obviously works, but when I remove the XAML and add the code in my UserControl Constructor; it doesn't.
Where am I going wrong?
Do I have to add some sort of Resource Dictionary?
How can I get it right to set the style of all Label's in my one specific UserControl?
You can try to create a style programatically and then add it to the resources.
Style style = new Style(typeof(Label));
style.Setters.Add(new Setter(Label.ForegroundProperty, Brushes.Blue));
Resources[typeof(Label)] = gridStyle;

Adding Tooltip to DataGrid Cells programmatically

i made different approaches to add tooltips to the cells of a DataGrid. I also found several information on this site, but i didn't make it work.
Here's what's the matter and what I've tried:
I have a DataGrid like:
DataGrid grid = new DataGrid();
Binding b = new Binding() { Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.Default, Source = AnObersableCollection, NotifyOnSourceUpdated = true, Path = new PropertyPath(".") } );
grid.SetBinding(DataGrid.ItemsSourceProperty, b);
I'd like that every cell has a tooltip with the cell content as tooltip content, so that truncated text is seen in the tooltip. So i took the CellStyles and created one like this:
Style CellStyle_ToolTip = new Style();
CellStyle_ToolTip.Setters.Add(new Setter(DataGridCell.ToolTipProperty, new ToolTip() { Content = "Yeah!" } ));
That works for static ToolTip contents, but how can I achieve that the ToolTip has the displayed cell content as content?
I found out that
CellStyle_ToolTip.Setters.Add(new Setter(DataGridCell.ToolTipProperty, new ToolTip().SetBinding(ToolTip.ContentProperty, b) ));
does not work and produces an "Cannot set expression. It is marked as "NonShareable" and has already been used"-Error, which makes quite sense as the Binding is already in use. I came to this approach (which probably was complete nonsense) through several other discussions that all use xaml, which is not an option for me. I also found the following solution but don't know how to use without xaml.
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}" />
</Style>
</DataGrid.CellStyle>
PS: All columns are DataGridTextColumns, except for one DataGridComboBoxColumn.
Using the CellStyle Property you could do:
Style CellStyle_ToolTip = new Style();
var CellSetter = new Setter(DataGridCell.ToolTipProperty, new Binding() {RelativeSource=new RelativeSource(RelativeSourceMode.Self), Path=new PropertyPath("Content.Text")});
CellStyle_ToolTip.Setters.Add(CellSetter);
grid.CellStyle = CellStyle_ToolTip;

Remove the default style of selected row

I know that question was ask a lot, but i only saw it in XAML code file.
I am working on a datagrid extension, so I am in C# code file and I would like to know how to remove the default style for selected row (In my case i want nothing change in the style, I have an image in row header that show wich row is selected).
Side question, can we have a selection like "Ctrl" is pressed and how in C# code?
Thanks
Edit
I try this code :
Style oRow = new Style(typeof(DataGridRow));
DataTrigger oTrigger2 = new DataTrigger();
Binding oBind = new Binding();
oBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridRow), 1);
oBind.Path = new PropertyPath(DataGridRow.IsSelectedProperty);
oTrigger2.Binding = oBind;
oTrigger2.Value = true;
oTrigger2.Setters.Add(new Setter(DataGridRow.BackgroundProperty, Brushes.Khaki));
oRow.Triggers.Add(oTrigger2);
this.RowStyle = oRow;
For now, i tryed to put the selected background in Khaki for test. But i get the old blue highlight.
Edit 2
Base on the idea of Sinatr, I change the DatagridRow for DatagridCell and ended with :
Style oRow = new Style(typeof(DataGridCell));
DataTrigger oTrigger2 = new DataTrigger();
Binding oBind = new Binding();
oBind.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridRow), 1);
oBind.Path = new PropertyPath(DataGridRow.IsSelectedProperty);
oTrigger2.Binding = oBind;
oTrigger2.Value = true;
oTrigger2.Setters.Add(new Setter(DataGridCell.BackgroundProperty, null));
oTrigger2.Setters.Add(new Setter(DataGridCell.BorderBrushProperty, null));
oRow.Triggers.Add(oTrigger2);
this.RowStyle = oRow;
I only need to get the foreground of the row to set the foreground of the cell. But i got a new question with that solution, is ok to set background to null or i should bind it to thr row background?
You have to replace CellStyle control template.
To example, this stupid templating
<DataGrid x:Name="dataGrid">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<TextBlock Text="1" Background="Khaki" Foreground="Red"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
</DataGrid>
will produce this output (for just 1 Tuple<string, string, string>, whatever):
It stays the same for selected or not selected item.
Obviously you have to implement it more properly, but given answer should give you an idea.

How to change the window.resource style setter?

<Window.Resources >
<Style x:Name="stylepropery" x:Key="BaseContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Foreground" Value="{DynamicResource MyFillBrush}" />
</Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseContentControlStyle}" />
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource BaseContentControlStyle}" />
</Window.Resources>
which is applying common font color to all the labels and textboxes now i want to change the color of font from code behind but some how its not applying
i just want to change setter propery value
Setter setter = new Setter(ContentControl.ForegroundProperty, dt.Rows[0]["value"]);
Style style = this.FindResource("BaseContentControlStyle") as Style;
style.Setters.Add(setter);
I have used this but not succedd
Try this code this may help you
Style style = new Style(typeof(ContentControl));
style.Setters.Add(new Setter(ContentControl.ForegroundProperty, Brushes.Green));
Resources["BaseContentControlStyle"] = style;
var converter = new System.Windows.Media.BrushConverter();
var brush = (Brush)converter.ConvertFromString(dt.Rows[0]["value"].ToString());
Style st = this.Resources["BaseContentControlStyle"] as Style;
Random r = new Random();
this.Resources["MyFillBrush"] = (new BrushConverter().ConvertFrom(dt.Rows[0]["value"].ToString()));
After lots of googling i have found the solution i am adding this solution for the future refrence users so they don't have to search many things :)

Add Items to ListBox and Style

I have a simple class:
public class Foo
{
public string Text { get; set; }
public bool AppleStyle { get; set; }
public Foo(string text, bool applyStyle)
{
Text = text;
ApplyStyle = applyStyle;
}
public override string ToString()
{
return Text;
}
}
Which is then used to add items to a ListBox:
var one = new Foo("Some Text", false);
var two = new Foo("More Text", true);
MyListBox.Items.Add(one);
MyListBox.Items.Add(two);
I then loop through the items in the ListBox to figure out how to style them. This is where I get stuck. I tried inheriting from ListBoxItem for the class, but no items get added if I do that.
for (int i = 0; i < MyListBox.Items.Count; i++)
{
if(((Foo)MyListBox.Items[i]).ApplyStyle)
{
((ListBoxItem)MyListBox.Items[i]).Style = Resources["MyStyle"] as Style;
}
}
Update:
In MainWindow.xaml:
<Window.Resources>
<Style x:Key="MyStyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="Bisque"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Style>
</Window.Resources>
Update 3:
Making some progress, just need to know how to refresh the styles (after clicking on a button). Plus if Resource is not in MainWindow.xaml, would it then look in App.xaml before returning null?
MainWindow.xaml
<Window...>
<Window.Resources>
<Style x:Key="MyClass" TargetType="ListBoxItem">
<Setter Property="Background" Value="Bisque"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Style>
<myapp:MyListItemStyleSelector x:Key="MyListItemStyleSelector" />
</Window.Resources>
<Grid>
...
<ListBox .... ItemContainerStyleSelector="{StaticResource: MyListItemStyleSelector}" />
...
</Grid>
</Window>
MyListItemStyleSelector.cs
public class MyListItemStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
int index = ic.ItemContainerGenerator.IndexFromContainer(container);
Style applyStyle = null;
var data = item as Foo;
if (data != null && data.ApplyStyle)
{
applyStyle = ic.TryFindResource("MyStyle") as Style;
}
return applyStyle;
}
}
I think you have some sort of mixup here, i try to explain as good as i can.
First of all You usually never need to change the Style in code, like your last code block.
One thing that is difficult to understand in the beginning is the use of a ItemContainerStyle and DataTemplate.
I would suggest that you do the following.
Instead of changing the style off your ListBoxItem see if it is sufficient to use a DataTemplate. The DataTemplate defines how the Content of your ListBoxItem is shown.
<DataTemplate TargetType="{x:Type Foo}">
<!-- your visuals and controls here -->
</DataTemplate>
Now if you want to use different datatemplates you could use different classes and create different DataTemplates for them, or you use a DataTemplateSelector
public class FooTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
var mdl = item as Foo;
if( mdl.AppleStyle )
return element.FindResource("appleTemplate") as DataTemplate;
return element.FindResource("normalTemplate") as DataTemplate;
}
}
Create that templateselector in xaml and reference it in your listbox
<myNs:FooTemplateSelector x:Key="fooTemplateSelector"/>
<Listbox DataTemplateSelector="{StaticResource fooTemplateSelector}"/>
now you need to create 2 DataTemplates appleTemplate *normalTemplate* and you can easyl distinguish which data template to use vial the selector. Which is done automatically in the ListBox for you.
If you really want to change the Style of the ItemContainer you can use ItemContainerStyleSelector which works similar to the DataTemplateSelector. But i would not suggest it. You should supply the content and leave the ListBoxItem as it is, only if you want to modify the design(in this case, the selection color etc.), otherwise it might confuse the user or break functionality.
If you add data-objects directly to the ListBox the container-items will be generated automatically, you cannot get them this way.
Use the ItemContainerGenerator:
((ListBoxItem)MyListBox.ItemContainerGenerator.ContainerFromIndex(i)).Style = Resources["MyStyle"] as Style;
Why not do this in the XAML?
<ListBox Name="MyListBox">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding ApplyStyle}" Value="True">
<Setter Property="Background" Value="Bisque" />
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
</ListBox>
But your overall problem is that ListBox.Items returns a collection of data objects, not XAML Controls. To get the XAML control that contains the Data Object you have to do as H.B. suggested and use MyListBox.ItemContainerGenerator.ContainerFromItem(dataObject) to get the XAML Container for the data object. Just be sure you wait until after the ItemContainerGenerator has finished rendering items to get the container (I believe it has a Status property or StatusChanged event you can use... it's been a while and I can't remember the exact syntax)

Categories