This question already has answers here:
How do you change Background for a Button MouseOver in WPF?
(6 answers)
Closed 12 months ago.
I'm trying to create a button from code and make the hover color change, for some reason the setters, etc are not getting applied. I'm pretty sure I'm either missing a step or it's something else but nothing changes, not even the color while not hovering. I'm not trying to make the button in XAML but create it in code and I know how to do it in XAML.
System.Windows.Controls.Button createPlaylist = new System.Windows.Controls.Button()
{
Height = 100,
Width = 100,
Margin = new Thickness(0,50,0,0)
};
// STYLE END //
var dt = new DataTemplate();
Style style = new Style(typeof(System.Windows.Controls.Button), createPlaylist.Style);
Trigger t = new Trigger();
t.Property = IsMouseOverProperty;
t.Value = true;
Setter setter = new Setter();
setter.Property = BackgroundProperty;
setter.Value = Brushes.Blue;
t.Setters.Add(setter);
Trigger s = new Trigger();
s.Property = IsMouseOverProperty;
s.Value = false;
Setter set = new Setter();
set.Property = BackgroundProperty;
set.Value = Brushes.Red;
s.Setters.Add(setter);
style.Triggers.Add(t);
createPlaylist.Style = style;
You have to manage the Background color of the Border within the Template of the Button, instead of just changing the Background property of the Button itself.
i.e. do the equivalent of this XAML:
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
Something like this on code behind:
Style buttonStyle = new Style();
FrameworkElementFactory contentBorder = new FrameworkElementFactory(typeof(Border));
contentBorder.AppendChild(new FrameworkElementFactory(typeof(ContentPresenter)));
contentBorder.SetBinding(Border.BackgroundProperty, new Binding { RelativeSource = RelativeSource.TemplatedParent, Path = new PropertyPath(nameof(Background)) });
ControlTemplate templateButton = new ControlTemplate(typeof(Button)) { VisualTree = contentBorder };
buttonStyle.Setters.Add(new Setter { Property = Button.TemplateProperty, Value = templateButton });
Trigger styleTrigger = new Trigger { Property = Button.IsMouseOverProperty, Value = true };
styleTrigger.Setters.Add(new Setter { Property = Button.BackgroundProperty, Value = Brushes.Blue });
buttonStyle.Triggers.Add(styleTrigger);
createPlaylist.Style = buttonStyle;
Related
I'm using accordion control.
<dxa:AccordionControl.Resources>
<Style TargetType="dxa:AccordionItem">
<Setter Property="Foreground" Value="Orange"/>
<Style.Triggers>
<Trigger Property="IsMouseOverHeader" Value="True">
<Setter Property="Foreground" Value="black"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</dxa:AccordionControl.Resources>
But I don't use it anymore ↑
because I changed the contents of the item dynamically.
Setter setter = new Setter();
Style style2 = new Style();
style2.TargetType = new AccordionItem().GetType();
setter.Property = AccordionItem.ForegroundProperty;
setter.Value = Brushes.Red;
style2.Setters.Add(setter);
ResourceDictionary resourceDictionary = new ResourceDictionary();
resourceDictionary.Add(style2.TargetType, style2);
//Trigger trigger = new Trigger();
//trigger.Property = AccordionItem.IsMouseOverHeaderProperty;
//trigger.Value = true;
accordionControlHistoryMenu.Resources = resourceDictionary;
How Can I express it this xamlcode convert xaml to C# source?
You don't need to store the style in a ResourceDictionary, just assign it directly:
Xaml:
<dxa:AccordionControl.Resources x:Name="myControl">
Code:
myControl.Style = style2;
While this answers your question, it's almost never the correct way to do this. Your styles should be binding to dynamic data that your view model layer is creating.
I have a simple WPF application which displays reddit links in a DataGrid:
Notice however that the link in the DataGridHyperlinkColumn isn't visible when a row is selected, due to the color of the link and the color of the row highlight.
What's a good way to resolve this? Change the link text color? Change the row highlight color?
If possible, please show your suggestion in terms of C# code as opposed to XAML as this application isn't using XAML. Otherwise, a XAML solution is fine; I'll just convert it to C#. :-)
For reference, here's the code used for the Title column:
var event_setter = new EventSetter()
{
Event = Hyperlink.ClickEvent,
Handler = (RoutedEventHandler)((sender, e) =>
{
System.Diagnostics.Process.Start((data_grid.SelectedItem as Link).Url);
})
};
var style = new Style();
style.Setters.Add(event_setter);
var hyperlink_column = new DataGridHyperlinkColumn()
{
Header = "Title",
Binding = new Binding("Title"),
ElementStyle = style,
Width = 600
};
data_grid.Columns.Add(hyperlink_column);
You could add an implicit Hyperlink style to your DataGrid:
const string Xaml = "<Style TargetType=\"Hyperlink\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">" +
"<Style.Triggers>" +
"<DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridCell}}\" Value=\"True\">" +
"<Setter Property=\"Foreground\" Value=\"White\" />" +
"</DataTrigger>" +
"</Style.Triggers>" +
"</Style>";
data_grid.Resources.Add(typeof(Hyperlink), System.Windows.Markup.XamlReader.Parse(Xaml) as Style);
data_grid.Columns.Add(hyperlink_column);
The Selector.IsSelected property of DataGridHyperLink Column can be used and when the selection on particular item changes you can update the style with trigger.
<DataGridHyperlinkColumn.CellStyle>
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="Foreground" Value="Blue"/>
<Style.Triggers>
<Trigger Property="Selector.IsSelected" Value="True">
<Trigger.Setters>
<!--change the value for the property based on your needs-->
<Setter Property="Foreground" Value="Yellow"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</DataGridHyperlinkColumn.CellStyle>
Pure XAML solution:
<DataGrid>
<DataGrid.Resources>
<Style TargetType="{x:Type Hyperlink}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridCell}}"
Value="True">
<DataTrigger.Setters>
<Setter Property="Foreground" Value="Yellow"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridHyperlinkColumn Width="180"
Header="Url"
Binding="{Binding Path=Uri, Mode=OneWay}" />
</DataGrid.Columns>
</DataGrid>
Here's a version of the answer provided by #mm8 converted from XAML to C#:
var data_trigger = new DataTrigger()
{
Binding = new Binding()
{
Path = new PropertyPath("IsSelected"),
RelativeSource = new RelativeSource() { AncestorType = typeof(DataGridCell) }
},
Value = true
};
data_trigger.Setters.Add(new Setter(ForegroundProperty, new SolidColorBrush(Colors.White)));
var style = new Style(typeof(Hyperlink));
style.Triggers.Add(data_trigger);
data_grid.Resources.Add(typeof(Hyperlink), style);
Here's a version of the answer provided by #mm8 converted from XAML to C# which uses some extension methods to avoid intermediate variables:
data_grid.Resources.Add(
typeof(Hyperlink),
new Style(typeof(Hyperlink))
.AddTrigger(
new DataTrigger()
{
Binding = new Binding()
{
Path = new PropertyPath("IsSelected"),
RelativeSource = new RelativeSource() { AncestorType = typeof(DataGridCell) }
},
Value = true
}
.AddSetter(new Setter(ForegroundProperty, new SolidColorBrush(Colors.White)))));
I have this code in my xaml which says to color my button when I hover my mouse and click my mouse over the button.
<Border x:Class="DatasetGrid.RowHeaderButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" MinWidth="30" Width="Auto">
<Border.Resources>
<SolidColorBrush x:Key="ButtOverBrush" Color="#53C3D5" Opacity="0.2"></SolidColorBrush>
<SolidColorBrush x:Key="ButtPressedBrush" Color="#53C3D5" Opacity="0.5"></SolidColorBrush>
</Border.Resources>
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource ButtOverBrush}"></Setter>
</Trigger>
<DataTrigger Binding="{Binding IsMouseDown, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource ButtPressedBrush}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
This works all well and good, but I find that as soon as I change the Background color in code behind, the above MouseOver and MouseDown triggers don't fire anymore.
RowHeaderButton rhb = RowHeadersColumn.VisibleRowHeaders[cell.CellInfo.RowIndex];
rhb.Background = new SolidColorBrush(Color.FromArgb(100, 83, 195, 213));
I'm quite new to WPF so I'm not sure what's going wrong.
Edit:
So to give some more information, my control above is a RowHeaderButton, i.e the row header to a grid. Each row in the grid has it's own row header button. So when the user hovers over or clicks it, it should change from white to the specified SolidColorBrush above.
In the code behind of another control, DataGrid.xaml.cs, I have the below code (simplified) which will change the color of the row header when when a cell in the same row of the grid is selected or not.
void UpdateSelectedCells() {
foreach (Cell cell in VisibleColumns.SelectMany(c => c.VisibleCells))
{
int cellRowIndex = cell.CellInfo.RowIndex;
cell.IsSelected = SelectedCells.Contains(cell.CellInfo);
foreach (RowHeaderButton rhb in RowHeadersColumn.VisibleRowHeaders)
{
int rowHeaderIndex = Convert.ToInt16(rhb._default.Text) - 1;
if (cellRowIndex == rowHeaderIndex)
{
if (cell.IsSelected)
{
rhb.Background = new SolidColorBrush(Color.FromArgb(100, 83, 195, 213));
}
else
{
bool rowselected = false;
//need to check if any other cell in the row is selected, if not then color row header white
foreach (CellInfo celll in SelectedCells)
{
if (celll.RowIndex == cellRowIndex)
{
rowselected = true;
break;
}
}
if (rowselected == false)
rhb.Background = Brushes.White;
}
}
}
}
}
I don't have a ViewModel for this.
The triggers are firing, but their setters are being overridden.
This is due to Dependency Property Value Precendence. If the Background property is set programmatically or as an attribute in the XAML, that value will override anything value any style setter gives it. In general, this is desirable behavior: You want to be able to override what the style does on an individual control.
The solution to this is to do all of your background brush changes in style triggers. Your code behind must have some reason for setting the background brush when it does. Whatever that is, find a way to do it with a trigger. Set a property on the viewmodel and write a trigger on that property.
If you need help translating that high level abstraction into your own code, please share enough code for me to understand why and where the codebehind is setting the Background, and what (if anything) you have for a viewmodel.
I solved the issue by creating a new Dependancy Property and binding it to a data trigger.
public bool IsCellSelected
{
get { return (bool)GetValue(IsCellSelectedProperty); }
set { SetValue(IsCellSelectedProperty, value); }
}
public static readonly DependencyProperty IsCellSelectedProperty =
DependencyProperty.Register("IsCellSelected", typeof(bool), typeof(RowHeaderButton), new PropertyMetadata(null));
In my xaml I have:
<DataTrigger Binding="{Binding IsCellSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource ButtPressedBrush}"></Setter>
</DataTrigger>
And in my code behind I set the value using:
RowHeaderButton rhb = RowHeadersColumn.VisibleRowHeaders[cell.CellInfo.RowIndex];
rhb.IsCellSelected = true; //or false
Now my button hover and button click events are not overridden.
I am working on a DLL that will place two hidden buttons in a WPF application.
Clicking those buttons on the right order will raise an event.
To remove the Mouse Over effect, I created a new style. I wanted the buttons to be completely transparent.
WPF Code
SecretCode.WPF secretCode = new SecretCode.WPF(testGrid, SecretCode.WPF.Location.Bottom, 75, 4000);
secretCode.SecretCodeActivated += secretCode_SecretCodeActivated;
APP.xaml
<Style x:Key="TransparentStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid Background="Transparent">
<ContentPresenter></ContentPresenter>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However, the issue is that I don't want to place that style on the WPF project as I want the DLL to be completely independent.
Secret Code DLL
public WPF(Grid grid, Location location, int size, int timeout)
{
Button leftButton = new Button();
leftButton.Width = size;
leftButton.Height = size;
leftButton.Margin = new Thickness(0, 0, 0, 0);
leftButton.HorizontalAlignment = HorizontalAlignment.Left;
leftButton.VerticalAlignment = location == Location.Top? VerticalAlignment.Top : VerticalAlignment.Bottom;
leftButton.Background = Brushes.Transparent;
leftButton.Style = Application.Current.FindResource("TransparentStyle") as Style;
leftButton.Click += leftPanel_Click;
grid.Children.Add(leftButton);
Button rightButton = new Button();
rightButton.Width = size;
rightButton.Height = size;
rightButton.Margin = new Thickness(0, 0, 0, 0);
rightButton.HorizontalAlignment = HorizontalAlignment.Right;
rightButton.VerticalAlignment = location == Location.Top ? VerticalAlignment.Top : VerticalAlignment.Bottom;
rightButton.Background = Brushes.Transparent;
rightButton.Style = Application.Current.FindResource("TransparentStyle") as Style;
rightButton.Click += rightPanel_Click;
grid.Children.Add(rightButton);
timeoutTimer.Interval = timeout;
timeoutTimer.Elapsed += timeoutTimer_Tick;
}
Is there any way to do that?
In some DLL:
<ResourceDictionary >
<Style x:Key="BoringButtonStyle" TargetType="{x:Type Button}">
//.....
</Style>
</ResourceDictionary>
On your application:
<Application ...>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Globals;component/Styles/ButtonStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
And then you can use it as a DynamicResource freely:
<UserControl ...>
<Grid>
<Button style="{DynamicResource BoringButtonStyle}"/>
</Grid>
</UserControl>
An alternative way to the code in app.xaml is:
ResourceDictionary dict = new ResourceDictionary();
System.Windows.Application.LoadComponent(dict, new System.Uri("/SomeAssembly;component/SomeResourceDictionary.xaml",System.UriKind.Relative));
This is what I could comeup with for you. Hope this helps.
Use the below string in your DLL class.
private string xamlText = "<Style xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"TransparentStyle\" TargetType=\"Button\">" +
"<Setter Property=\"Template\">" +
" <Setter.Value> " +
"<ControlTemplate TargetType = \"Button\">" +
"<Border>" +
"<Border.Style>" +
"<Style TargetType=\"{x:Type Border}\">" +
"<Style.Triggers>" +
"<Trigger Property = \"IsMouseOver\" Value=\"True\">" +
"<Setter Property = \"Background\" Value=\"Transparent\"/>" +
"</Trigger>" +
"</Style.Triggers>" +
"</Style>" +
"</Border.Style>" +
"<Grid Background = \"Transparent\">" +
"<ContentPresenter></ContentPresenter>" +
"</Grid>" +
"</Border>" +
"</ControlTemplate>" +
"</Setter.Value>" +
"</Setter>" +
"</Style>";
Add the following code at the top of the WPF method
var stringReader = new StringReader(xamlText);
var xmlReader = XmlReader.Create(stringReader);
var style = XamlReader.Load(xmlReader) as Style;
Next, just do like so:
leftButton.Style = style;
and
rightButton.Style = style;
I had once an extreme case I had to use such method so I pulled it out.
In general, when you create a library, you define its setting set. These settings are specified through methods. However, you need to phrase your problem in the most general way you can. You need a way to set the pattern expected (the right order), a history member to check whether the pattern is being matched and styling/class attributes. You can store the settings into an ini file or an XML or into the memory of your program or under the hood inside the DLL.
I want all of my charts to line up using the DateTimeContinuousAxis. I have several values logged with a time stamp. I parse the file and store the values per time stamp. Now how can I create several charts that will line up as if it were to look like a timing diagram using Telerik RadChartView? Mind you, this all will need to be done pragmatically aside from any styles that are set.
Here are some things to consider:
when the VerticalAxis label has 4 characters (-900), vs. 2 characters (10), the vertical axis of all the charts aren't lined up
when I Hide the HorizontalAxis.Visibility it gets rid of the axis like I would like, but then the LineSeries extends all the way to the right of the grid, so they don't line up with the bottom-most chart's HorizontalAxis
Here is a screenshot of what I'm talking about (and actually I have the vertical axis label thing partially fixed in this screenshot, see the answer on how I did it):
First thing is first, you have add each of your charts to the grid. Here is what my XAML looks like for that:
<Grid Name="layout" />
Next I initialize and setup a couple (not all are shown) components inside of my loop--that iterates through the collection or values that I want to chart--that I will be adding to the Grid/Chart
LineSeries lineSeries = new LineSeries();
lineSeries.VerticalAxis = new LinearAxis()
{ //set additional properties here
LabelStyle = Resources["AdjustedLabelVerticalAxis"] as Style
};
lineSeries.ItemsSource = collection.Values;
lineSeries.CategoryBinding = new PropertyNameDataPointBinding() { PropertyName = "Date" };
lineSeries.ValueBinding = new PropertyNameDataPointBinding() { PropertyName = "Value" };
RadCartesianChart chart = new RadCartesianChart();
chart.HorizontalAxis = new DateTimeContinuousAxis() { LabelFormat = "HH:mm:ss" };
chart.Series.Add(lineSeries);
Then I "hack" my HorizontalAxis to disappear:
chart.HorizontalAxis.MajorTickStyle = Resources["HideTicksHorizontalAxis"] as Style;
chart.HorizontalAxis.LabelStyle = Resources["HideShiftHorizontalAxisLabel"] as Style;
chart.HorizontalAxis.LineThickness = 0;
Now we need to programmatically add the chart to the grid at the end of the loop, so each chart is in their own row, which helps for auto-resizing.
layout.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(chart.MinHeight, GridUnitType.Star) });
Next we set what row in the Grid we want to put the chart, and add it:
Grid.SetRow(chart, i); //where i is the loop counter
layout.Children.Add(chart);
When the loop is done, we have all our charts in the collection to the grid, with no horizontal axes labeling. We need an DateTimeContinousAxis, so lets get crafty.
We first need to add the very last row, because we are going to create another chart and populate the same data, but then hide and adjust everything.
So outside of my loop I did:
layout.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(30) }); //add last row
DateTimeContinuousAxis graph = new DateTimeContinuousAxis();
graph.MinHeight = 12;
graph.Maximum = collection.Values[collection.Values.Count - 1].Date; //max/min DateTime values
graph.Minimum = collection.Values[0].Date;
graph.LabelInterval = 2;
graph.MaximumTicks = 3;
graph.LabelFormat = "hh:mm:ss";
graph.MajorStepUnit = Telerik.Charting.TimeInterval.Second;
graph.LineThickness = 1;
Then go on to create a minimalist LinearAxis, LineSeries, and RadCartesianChart
LinearAxis axis = new LinearAxis();
LineSeries ls = new LineSeries();
RadCartesianChart blankChart = new RadCartesianChart();
ls.ItemsSource = collection.Values; //Set up for binding
ls.CategoryBinding = new PropertyNameDataPointBinding() { PropertyName = "Date" };
ls.ValueBinding = new PropertyNameDataPointBinding() { PropertyName = "Value" };
ls.Visibility = System.Windows.Visibility.Hidden; //hide the line from being plotted
axis.LabelStyle = Resources["TextBlockStyle2"] as Style;
axis.Opacity = 0; //make it invisible
axis.MinHeight = 0; //make it able to resize to 0 if ever needed
blankChart.MinHeight = 0;
blankChart.HorizontalAxis = graph;
blankChart.VerticalAxis = axis;
blankChart.Series.Add(ls);
Grid.SetRow(blankChart, collection.Count);
layout.Children.Add(blankChart);
There you have it, the last Grid in your window will contain just a visible DateTimeContinuous axis that will line up with your other charts. This is quite the hack, so its not the prettiest or revised. Below is the Styles that can be used in your XAML as well as the final result.
<Style x:Key="AdjustedLabelVerticalAxis" TargetType="{x:Type TextBlock}">
<Setter Property="Width" Value="30"/>
<Setter Property="Margin" Value="0,0,2,0"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="TextAlignment" Value="Right"/>
</Style>
<Style x:Key="HideShiftHorizontalAxisLabel" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="4,-15,4,0"/>
<Setter Property="Foreground" Value="Transparent"/>
</Style>
<Style x:Key="HideTicksHorizontalAxis" TargetType="{x:Type Rectangle}">
<Setter Property="Visibility" Value="Hidden"/>
</Style>
Any questions, please ask.