I want to display a MetroDialog with a custom background color, for error messages.
I tried using the property CustomResourceDictionary without luck:
Function to display message:
public static async Task<MessageDialogResult> DialogOK(string title, string message)
{
MetroDialogSettings settings = new MetroDialogSettings();
settings.CustomResourceDictionary = new ResourceDictionary { Source = new Uri("pack://application:,,,/MyApp;component/Resources/ErrorDialogRD.xaml") };
var result = await mainWindow.ShowMessageAsync(title, message, MessageDialogStyle.Affirmative, settings );
return result;
}
Resource dictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Dialogs="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro">
<Style TargetType="{x:Type Dialogs:BaseMetroDialog}">
<Setter Property="Background" Value="Red" />
</Style>
</ResourceDictionary>
What am I missing? :)
Related
my problem is that the embedded dlls of MahApps.Metro and MahApps.Metro.IconPacks are not working fine.
Used the Visual Studio debugger to check if they are getting loaded and it worked fine. If it could not load them the program would throw a xaml exception.
But for some reason the ResourceDictionaries which are merged are not working in runtime.
Should look like this
But looks like this
(As you can see the text color is different and it is missing the icon on the right side => does not load the styles.)
It looks like the first picture if both Dll's are provided in the directory of the program.
My App.xaml
<Application x:Class="Launcher.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:Launcher.Class.Converter"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatSlider.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro.IconPacks;component/Themes/IconPacks.xaml" />
<ResourceDictionary Source="Controls\ColorBrushes.xaml"/>
<ResourceDictionary Source="Controls\CustomMetroWindow.xaml"/>
<ResourceDictionary Source="Controls\CustomListView.xaml"/>
<ResourceDictionary Source="Controls\NewsStyle.xaml"/>
<ResourceDictionary Source="Controls\TextImageBox.xaml"/>
<ResourceDictionary Source="Controls\GlowMetroButton.xaml"/>
<ResourceDictionary Source="Controls\ToggleSwitchWin10.xaml"/>
<ResourceDictionary Source="Simple Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<converter:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<converter:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converter:BooleanToVisibilityCollapsedConverter x:Key="BoolToVisibilityCollapsedConverter" />
<converter:MultiObjectToBooleanConverter x:Key="MultiObjectToBooleanConverter" />
<converter:DownloadProgressToVisibilityConverter x:Key="DownloadProgressToVisibilityConverter" />
<converter:MultiObjectToStatusBarColorConverter x:Key="MultiObjectToStatusBarColorConverter" />
<converter:MultiBooleanConverter x:Key="MultiBooleanConverter" />
<converter:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibilityConverter" />
<converter:OpacityToBooleanConverter x:Key="OpacityToBooleanConverter" />
</ResourceDictionary>
</Application.Resources>
My Program.cs
[STAThread]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resourceName = Assembly.GetExecutingAssembly().GetName().Name + ".Dll." + new AssemblyName(args.Name).Name + ".dll";
if (!resourceName.Contains("resources"))
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream != null)
{
var assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
}
else
{
Assembly requestedAssembly = args.RequestingAssembly;
AssemblyName requestedAssemblyName = new AssemblyName(args.Name);
while (true)
{
// requesting name in format: %assemblyname%.resources
// rewrite to: %assemblyName%.%assemblyName%.%culture%.resources.dll
//
var baseName = requestedAssemblyName.Name.Substring(0, requestedAssemblyName.Name.Length - ".resources".Length);
var name = string.Format("{0}.Dll.Lang.{1}.{2}.resources.dll", baseName, requestedAssemblyName.CultureInfo.Name, Assembly.GetExecutingAssembly().GetName().Name);
// by default for resources the requestingAssembly will be null
Assembly asm = null;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// resources have the same name as their belonging assembly, so find by name
var parentName = requestedAssemblyName.Name.Substring(0, requestedAssemblyName.Name.Length - ".resources".Length);
// I'd love to use linq here, but Cecil starts fucking up when I do (null reference exception on assembly.Write)
// without a Linq query it works fine, though
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var assembly in assemblies)
{
if (assembly.GetName().Name == parentName)
{
asm = assembly;
}
}
if (asm == null)
{
// cannot find assembly from which to load
return null;
}
using (var stream = asm.GetManifestResourceStream(name))
{
if (stream != null)
{
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
// did not find the specific resource yet
// attempt to use the parent culture, this follows the .Net resource fallback system
// e.g. if sub resource de-DE is not found, then .Parent will be "de", if that is not found parent will probably be default resource
var fallback = requestedAssemblyName.CultureInfo.Parent.Name;
if (string.IsNullOrEmpty(fallback))
{
// is empty if no longer a parent
// return null so .Net can load the default resource
return null;
}
var alteredAssemblyName = requestedAssemblyName.FullName;
alteredAssemblyName = alteredAssemblyName.Replace(string.Format("Culture={0}", requestedAssemblyName.CultureInfo.Name), string.Format("Culture={0}", fallback));
requestedAssemblyName = new AssemblyName(alteredAssemblyName);
}
}
return null;
};
App.Main();
}
The control
<TextBox x:Name="Username"
Controls:TextBoxHelper.Watermark="{lex:Loc Key=LoginWindow.YourUsername}" Margin="0,20,0,9"
Text="{Binding Config.AuthUsername}"
IsEnabled="{Binding LoggingIn, Converter={StaticResource InverseBooleanConverter}}" TextAlignment="Justify"
>
<TextBox.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource MetroTextImageBox}">
<Setter Property="Controls:TextBoxHelper.ButtonTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid Background="{TemplateBinding Background}">
<Rectangle>
<Rectangle.Fill>
<VisualBrush>
<VisualBrush.Visual>
<iconPacks:PackIconModern Kind="User" Foreground="{StaticResource MainIconBrush}" />
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Resources>
</TextBox>
This code works for me: (add all libraries as Embedded Resource and set startup object to this class)
public class Program
{
private static Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();
private static string[] EmbeddedLibraries =
ExecutingAssembly.GetManifestResourceNames().Where(x => x.EndsWith(".dll")).ToArray();
[STAThreadAttribute]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
App.Main();
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// Get assembly name
var assemblyName = new AssemblyName(args.Name).Name + ".dll";
// Get resource name
var resourceName = EmbeddedLibraries.FirstOrDefault(x => x.EndsWith(assemblyName));
if (resourceName == null)
{
return null;
}
// Load assembly from resource
using (var stream = ExecutingAssembly.GetManifestResourceStream(resourceName))
{
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
}
The project file portion was cut off previous answer.
Add this to project file
<!--Then add this is project file.-->
<Target Name="EmbedReferencedAssemblies" AfterTargets="ResolveAssemblyReferences">
<ItemGroup>
<!-- get list of assemblies marked as CopyToLocal -->
<AssembliesToEmbed Include="#(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" />
<!-- add these assemblies to the list of embedded resources -->
<EmbeddedResource Include="#(AssembliesToEmbed)">
<LogicalName>%(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Message Importance="high" Text="Embedding: #(AssembliesToEmbed->'%(DestinationSubDirectory)%(Filename)%(Extension)', ', ')" />
</Target>
<!-- no need to copy the assemblies locally anymore -->
<Target Name="_CopyFilesMarkedCopyLocal" />
I had the same issue you described and came across fix that resolves the issue.
public partial class App : Application {
public App() {
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) {
var executingAssembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(args.Name);
var path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) {
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (var stream = executingAssembly.GetManifestResourceStream(path)) {
if (stream == null)
return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
}
%(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)
'%(DestinationSubDirectory)%(Filename)%(Extension)',
', ')" />
I tried to create a Style for DataGridTextColumn with the following code
<Style TargetType="{x:Type DataGridTextColumn}">
...
</Style>
However, Visual Studio 2010 highlights {x:Type DataGridTextColumn} with a blue line and elaborates: Exception has been thrown by the target of an invocation.
Why does this happen and how do I fix it?
You can't style the DataGridTextColumn because DataGridTextColumn does not derive from FrameworkElement (or FrameworkContentElement). Only FrameworkElement, etc supports styling.
When you attempt to create a style in XAML for any type that is not a FrameworkElement or FrameworkContentElement you get that error message.
How do you solve this? As with any problem, where there is a will there is a way. In this case I think the easiest solution is to create an attached property for DataGrid to assign a DataGridColumn style:
<DataGrid ...>
<local:MyDataGridHelper.TextColumnStyle>
<Style TargetType="FrameworkElement">
... setters here ...
</Style>
</local:MyDataGridHelper.TextColumnStyle>
...
The implementation would be something along these lines:
public class MyDataGridHelper : DependencyObject
{
// Use propa snipped to create attached TextColumnStyle with metadata:
... RegisterAttached("TextColumnStyle", typeof(Style), typeof(MyDataGridHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var grid = (DataGrid)obj;
if(e.OldValue==null && e.NewValue!=null)
grid.Columns.CollectionChanged += (obj2, e2) =>
{
UpdateColumnStyles(grid);
}
}
}
private void UpdateStyles(DataGrid grid)
{
var style = GetTextColumnStyle(grid);
foreach(var column in grid.Columns.OfType<DataGridTextColumn>())
foreach(var setter in style.Setters.OfType<Setter>())
if(setter.Value is BindingBase)
BindingOperations.SetBinding(column, setter.Property, setter.Value);
else
column.SetValue(setter.Property, setter.Value);
}
}
The way this works is, any time the attached property is changed, a handler is added for the Columns.CollectionChanged event on the grid. When the CollectionChanged event fires, all columns are updated with the style that was set.
Note that the above code does not handle the situation where a style is removed and re-added gracefully: Two event handlers are registered. For a really robust solution you would want to fix this by adding another attached property containing the event handler so the event handler could be unregistered, but for your purpose I think this is unimportant.
Another caveat here is that the direct use of SetBinding and SetValue will cause the DependencyProperty to have a BaseValueSource of Local instead of DefaultStyle. This will probably make no difference in your case but I thought I should mention it.
The style tag has to go in the right place. Your datagrid may look something like this right now:
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn />
</DataGrid.Columns>
</DataGrid>
You might initially try to add the style tag directly within the DataGridTextColumn element which will not work. You can however create elements for "DataGridTextColumn.ElementStyle" and or "DataGridTextColumn.EditingElementStyle" just within the "DataGridTextColumn" element. Each of those element tags can then have style tags within them:
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn>
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Background" Value="Green"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Orange"></Setter>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
One style will be applied to viewing and the other will be applied when the cell is in edit mode. Note that it changes from a TextBlock when viewing to a TextBox when editing (This got me at first!).
This is more an addition to Ray Burns answer. I first wasn't able to implement it on my own, but with help of mm8 (https://stackoverflow.com/a/46690951/5381620) I got it running. Works really fine. For other people who have problems following this attached property approach maybe a full code snippet is helpful.
public class MyDataGridHelper : DependencyObject
{
private static readonly DependencyProperty TextColumnStyleProperty = DependencyProperty.RegisterAttached("TextColumnStyle", typeof(Style), typeof(MyDataGridHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var grid = (DataGrid)obj;
if (e.OldValue == null && e.NewValue != null)
grid.Columns.CollectionChanged += (obj2, e2) =>
{
UpdateColumnStyles(grid);
};
}
});
public static void SetTextColumnStyle(DependencyObject element, Style value)
{
element.SetValue(TextColumnStyleProperty, value);
}
public static Style GetTextColumnStyle(DependencyObject element)
{
return (Style)element.GetValue(TextColumnStyleProperty);
}
private static void UpdateColumnStyles(DataGrid grid)
{
var origStyle = GetTextColumnStyle(grid);
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
{
//may not add setters to a style which is already in use
//therefore we need to create a new style merging
//original style with setters from attached property
var newStyle = new Style();
newStyle.BasedOn = column.ElementStyle;
newStyle.TargetType = origStyle.TargetType;
foreach (var setter in origStyle.Setters.OfType<Setter>())
{
newStyle.Setters.Add(setter);
}
column.ElementStyle = newStyle;
}
}
}
xaml
<Grid>
<DataGrid Name="MyDataGrid" ItemsSource="{Binding Lines}" AutoGenerateColumns="False" >
<local:MyDataGridHelper.TextColumnStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</local:MyDataGridHelper.TextColumnStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="ProductId1" Binding="{Binding Path=Result1}" />
<DataGridTextColumn Header="ProductId2" Binding="{Binding Path=Result2}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Edit: In first approach I did overwrite the whole style. In new version it is still possible to maintain other styles modifications like this one
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</DataGridTextColumn.ElementStyle>
A small addition to pedrito answer. Everything works fine, but if origStyle has base style, base style's setters get discarded.
To fix this, we need get all setters:
private static void UpdateColumnStyles(DataGrid grid)
{
var origStyle = GetTextColumnStyle(grid);
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
{
//may not add setters to a style which is already in use
//therefore we need to create a new style merging
//original style with setters from attached property
var newStyle = new Style();
newStyle.BasedOn = column.ElementStyle;
newStyle.TargetType = origStyle.TargetType;
var baseSetters = GetBaseSetters(origStyle);
var allSetters = baseSetters.Concat(origStyle.Setters.OfType<Setter>());
foreach (var setter in allSetters)
{
newStyle.Setters.Add(setter);
}
column.ElementStyle = newStyle;
}
}
private static IEnumerable<Setter> GetBaseSetters(Style style)
{
return style.BasedOn?.Setters.OfType<Setter>().Concat(GetBaseSetters(style.BasedOn)??new Setter[0]);
}
More simple:
<FontFamily x:Key="DefaultFont">Snap ITC</FontFamily>
<Style x:Key="ControlStyle" TargetType="Control">
<Setter Property="FontFamily" Value="{StaticResource DefaultFont}"/>
</Style>
<Style TargetType="{x:Type DataGridCellsPresenter}" BasedOn="{StaticResource ControlStyle}">
</Style>
A DataGridTextColumn is nothing but a column with a TextBlock in it. Write a style with the TargetType as TextBlock and bind the ElementStyle property of the DataGridTextColumn to it. Hope that helps!
My user control have the following DP:
public static readonly DependencyProperty ButtonAnimationColorProperty =
DependencyProperty.Register("ButtonAnimationColor", typeof(Color), typeof(MyControl),
new FrameworkPropertyMetadata(Colors.RoyalBlue, FrameworkPropertyMetadataOptions.AffectsRender, ThemeUpdate));
public Color ButtonAnimationColor
{
get { return (Color)GetValue(ButtonAnimationColorProperty ); }
set { SetValue(ButtonAnimationColorProperty , value); }
}
This control is compiled into a dll, that I use in others solutions. It works perfect well when I set directly:
<ns:MyControl ButtonAnimationColor="Green" />
The problem occurs when I try to set this DP by using a Style Setter, like that:
<ns:MyControl>
<ns:MyControl.Style>
<Style>
<Setter Property="ButtonAnimationColor" Value="Green" />
</Style>
</ns:MyControl.Style>
</ns:MyControl>
It give me the following error:
The member "ButtoAnimationColor" is not recognized or is not acessible.
What changes I need to make in my code to be able to set the property like that?
Try setting the target type for the style:
<ns:MyControl.Style>
<Style TargetType="{x:Type ns:MyControl}">
<Setter Property="ButtonAnimationColor" Value="Green" />
</Style>
</ns:MyControl.Style>
How can i load style from below xaml using XamlReader.Load()
<Window
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys"
>
<ResourceDictionary>
<Style x:Key="LastRowHighlighted"
BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=RowStyle}}"
TargetType="{x:Type dxg:GridRowContent}">
</Style>
</ResourceDictionary>
Try something like this:
public void LoadStyle(string fileName)
{
if (File.Exists(fileName))
{
try
{
using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
ResourceDictionary resourceDictionary = (ResourceDictionary)XamlReader.
Load(fileStream);
Resources.MergedDictionaries.Clear(); // optional
Resources.MergedDictionaries.Add(resourceDictionary);
}
}
catch { }
}
}
I have a WPF CustomControl that is derived from ComboBox and I'm trying to figure out how to customize the display of the items. Basically, I want most items to show with normal text but,
depending on the data in each item object, I would like some to display either bold or italic. Normally I do this directly in the XAML, but since it's a CustomControl I'm a bit at a loss. I would love to be able to just bind it directly in code, but I'm also open to methods that might mean loading in external XAML styles, if you can show me how to do that (I haven't a clue).
The code below is a basic approximation of the control I'm using but greatly simplified. However, the basic concept of how the data loaded is the same and the data objects themselves are coming from an external source so they can not access the control itself in anyway. The template needs to
just be bound off of the base properties.
public class FormatData
{
public FormatData() { }
public string Name { get; set; }
public bool Bold { get; set; }
public bool Italic { get; set; }
}
public class FormatDropDown : System.Windows.Controls.ComboBox
{
public FormatDropDown()
{
}
public void LoadSelection(FormatData[] data)
{
try
{
this.ItemsSource = data;
this.DisplayMemberPath = "Name";
}
catch (Exception e) { MessageBox.Show(e.Message); ; }
}
}
The control is populated as follows:
var data = new FormatData[]{
new FormatData(){
Name = "Normal"
},
new FormatData(){
Name = "Bold",
Bold = true
},
new FormatData(){
Name = "Italic",
Italic = true
},
new FormatData(){
Name = "BoldItalic",
Bold = true,
Italic = true
},
};
fddTest.LoadSelection(data);
Anyone have an idea of how I can achieve this?
How about this:
public class FormatDropDown : System.Windows.Controls.ComboBox {
static FormatDropDown() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(FormatDropDown), new FrameworkPropertyMetadata(typeof(FormatDropDown)));
}
public void LoadSelection(FormatData[] data) {
try {
this.ItemsSource = data;
this.DisplayMemberPath = "Name";
} catch (Exception e) { MessageBox.Show(e.Message); ; }
}
}
And in the theme file (generic.xaml):
<Style TargetType="{x:Type local:FormatDropDown}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Bold}" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
<DataTrigger Binding="{Binding Italic}" Value="True">
<Setter Property="FontStyle" Value="Italic" />
</DataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>
So essentially override the DefaultStyleKey for your custom control.