I am using visual studio dark theme. As a result when designing my views I cannot see the font if its black. A fix will be to set the background of the view to white. But our application has different themes so I cannot hard code that.
There are to great properties that I use when creating an usercontrol:
d:DesignWidth="1110" d:DesignHeight="400"
those properties are only affecting the view at design time. It will be great if I can create a property d:DesignBackground just so that I do not have to be adding and removing the background property every time I run the application.
Not sure if it's exactly what you're looking for, but what I do is just plop a trigger in the app.xaml to invoke using the IsInDesignMode property like;
Namespace (Thanks Tono Nam);
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=PresentationFramework"
XAML;
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<Trigger Property="ComponentModel:DesignerProperties.IsInDesignMode"
Value="True">
<Setter Property="Background"
Value="#FFFFFF" />
</Trigger>
</Style.Triggers>
</Style>
Simple, but works, and sometimes I target other dependency properties like font and stuff too depending on the need. Hope this helps.
PS - You can target other TargetType's with their own properties the same way, like for example, ChildWindows, Popups, Windows, whatever...
You can create a static class with an attached property for design mode:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Helpers.Wpf
{
public static class DesignModeHelper
{
private static bool? inDesignMode;
public static readonly DependencyProperty BackgroundProperty = DependencyProperty
.RegisterAttached("Background", typeof (Brush), typeof (DesignModeHelper), new PropertyMetadata(BackgroundChanged));
private static bool InDesignMode
{
get
{
if (inDesignMode == null)
{
var prop = DesignerProperties.IsInDesignModeProperty;
inDesignMode = (bool) DependencyPropertyDescriptor
.FromProperty(prop, typeof (FrameworkElement))
.Metadata.DefaultValue;
if (!inDesignMode.GetValueOrDefault(false) && Process.GetCurrentProcess().ProcessName.StartsWith("devenv", StringComparison.Ordinal))
inDesignMode = true;
}
return inDesignMode.GetValueOrDefault(false);
}
}
public static Brush GetBackground(DependencyObject dependencyObject)
{
return (Brush) dependencyObject.GetValue(BackgroundProperty);
}
public static void SetBackground(DependencyObject dependencyObject, Brush value)
{
dependencyObject.SetValue(BackgroundProperty, value);
}
private static void BackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!InDesignMode)
return;
d.SetValue(Control.BackgroundProperty, e.NewValue);
}
}
}
And you can use it like this:
xmlns:wpf="clr-namespace:Helpers.Wpf;assembly=Helpers.Wpf"
<Grid Background="Black"
wpf:DesignModeHelper.Background="White">
<Button Content="Press me!"/>
</Grid>
You can use this approach to implement other property for design mode.
In 2022 you actually can and almost guessed it:
d:Background="White"
Note you can set pretty much any other control properties differently for design-time when prefix them with d:, for example:
<TextBlock Text="{Binding TextFromViewModel}"
Foreground="{StaticResource PrimaryBrush}"
d:Text="Design-time text"
d:Foreground="Black" />
Source: Use Design Time Data with the XAML Designer in Visual Studio
Related
I'm trying to learn about "Attached Behaviors" and have run into a bit of a problem. The article I'm currently using is: http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/
In a nutshell I would like to be able to "click" on an entry in a TreeView and have this bring up a file in Notepad. I have this code to bring up a file in Notepad working via a Command Button in WPF. For now let's call this command button...Testing. Here's the XAML I'm using to bind this button to the View.
<TreeView ItemsSource="{Binding Courses}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="local:CommandBehavior.Event" Value="MouseDoubleClick"/>
<Setter Property="local:CommandBehavior.Action" Value="{Binding Path=TestIng}"/>
<Setter Property="local:CommandBehavior.CommandParameter" Value="ShowThis" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
When I double-click on an entry in the TreeView it does indeed execute the code: "TestIng", but it bombs on the following line:
strategy.Execute(CommandParameter)
The actual error is:
Object reference not set to an intance of an object
so this tells me that I have perhaps not set up the "CommandParameter" properly.
Here's the Model with the definition of the "TestIng" Command Button.
using System.Collections.ObjectModel;
using System.Linq;
using BusinessLib;
using System.Windows.Input;
using System.Windows;
using System;
using System.Collections.Generic;
namespace TreeViewWithViewModelDemo.LoadOnDemand
{
public class PublicationsViewModel
{
readonly ReadOnlyCollection<CourseViewModel> _courses;
readonly ICommand _searchCommand;
public PublicationsViewModel(Course[] courses)
{
_courses = new ReadOnlyCollection<CourseViewModel>(
(from course in courses
select new CourseViewModel(course))
.ToList());
_searchCommand = new TestIng(this);
}
public ReadOnlyCollection<CourseViewModel> Courses
{
get { return _courses; }
}
public ICommand SearchCommand
{
get { return _searchCommand; }
}
}
class TestIng : ICommand
{
public TestIng(PublicationsViewModel param1)
{
}
public bool CanExecute(object parameter)
{
return true;
}
event EventHandler ICommand.CanExecuteChanged
{
add { }
remove { }
}
public void Execute(object parameter)
{
MessageBox.Show("Into This Here");
}
public void ShowThis()
{
MessageBox.Show("Into This Here Number2");
}
}
}
I know that it is not probably going to be a matter of you giving me the exact answer given the fact I'm only giving you part of the code, but I wonder if you wouldn't mind giving me some ideas on how to proceed and what I might try next.
I think the error is in how I'm defining the CommandParameter in the XAML. I'm really not sure if I understand how this works, but I think this is the method in the class that you want to have executed when the "MouseDoubleClick" is pressed. I have two methods in the ViewModel:
public void Execute(object parameter)
public void ShowThis()
Which I am using for simple testing...but these messagebox.show lines don't get executed. Like I said, I'm getting an error...
Can someone provide me with some insight in how I should define:
<Setter Property="local:CommandBehavior.CommandParameter" Value="ShowThis" />
Or what I could possibly look into in order to proceed. I'm stuck...
Thanks
Change your command binding to point to SearchCommand instead of TestIng
Your sample code shows TestIng as a class, not a property, and you need to bind to the property
In WPF XAML there is the convenient DesignHeight and DesignWidth, for instance in code as
<UserControl ... d:DesignHeight="500" d:DesignWidth="500" ... />
which is great because I can build the layout with a representative, but not locked-in, control size.
However, I'm often building dark UIs, where labels and so forth need to be white, but my controls still need a transparent background color. This creates a design-time inconvenience because white seems to be the default background color for transparent controls in the designer, leading to unreadable white-on-white labels.
Is there a way or strategy for setting the design-time background color, with similar convenience as DesignHeight/DesignWidth?
There's an undocumented property d:DesignStyle of type Style that you can set on a user control. This style is only applied in the designer and is not used at runtime.
You use it like this:
<UserControl ... d:DesignStyle="{StaticResource MyDesignStyle}" />
Or like this:
<UserControl ...>
<d:DesignerProperties.DesignStyle>
<Style TargetType="UserControl">
<Setter Property="Background" Value="White" />
<Setter Property="Height" Value="500" />
<Setter Property="Width" Value="500" />
</Style>
</d:DesignerProperties.DesignStyle>
</UserControl>
The Background property is what you asked for. The Height and Width do replace your d:DesignHeight=500 and d:DesignWidth=500 in the <UserControl> tag. Then you have all your design properties at one place.
Note however, that any value set on the Style property (the one used at runtime) will also override the DesignStyle in the designer.
I found that you can do one for yourself. Custom design-time attributes in Silverlight and WPF designer is a tutorial how to do it for both Silverlight and WPF.
My answer was found here: Black Background for XAML Editor. There are a number of choices including checking System.ComponentModel.DesignerProperties.GetIsInDesignMode(this) at runtime.
This is the complete solution for DesignBackground:
public class DesignTimeProperties : DependencyObject
{
private static readonly Type OwnerType = typeof(DesignTimeProperties);
#region DesignBackground (attached property)
public static Brush GetDesignBackground(DependencyObject obj)
{
return (Brush)obj.GetValue(DesignBackgroundProperty);
}
public static void SetDesignBackground(DependencyObject obj, Brush value)
{
obj.SetValue(DesignBackgroundProperty, value);
}
public static readonly DependencyProperty DesignBackgroundProperty =
DependencyProperty.RegisterAttached(
"DesignBackground",
typeof (Brush),
OwnerType,
new FrameworkPropertyMetadata(Brushes.Transparent,
DesignBackgroundChangedCallback));
public static void DesignBackgroundChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (IsInDesignMode)
{
var control = d as Control;
var brush = e.NewValue as Brush;
if (control != null && brush != null)
{
control.Background = brush;
}
}
}
public static bool IsInDesignMode
{
get
{
return
((bool)
DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof (DependencyObject)).DefaultValue);
}
}
#endregion
}
Usage:
<UserControl ... infra:DesignTimeProperties.DesignBackground="Black" />
The d:DesignerProperties.DesignStyle technique shown on this page works great for applying a WPF design-time-only style to a single control, but it doesn't appear to work for a Style in a ResourceDictionary that would apply to all of the appropriately-typed controls or elements under the scope of the dictionary. Below is simple solution I found for deploying a designer-only style into a ResourceDictionary.
Consider for example a Window containing a TreeView, where we want the TreeViewItem nodes to show as fully expanded—but only at design time. First, put the desired style in the XAML dictionary in the normal way.
<Window.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</Window.Resources>
Here, the Style is put in the ResourceDictionary of the Window but of course you could use any other subsuming dictionary instead. Next, in the C# code, remove the style from the ResourceDictionary when design mode is not detected. Do this is in the OnInitialized override:
protected override void OnInitialized(EventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this) == false)
Resources.Remove(typeof(TreeViewItem));
base.OnInitialized(e);
}
Design Mode: Runtime Mode:
I am trying to access commands that are defined in MainWindow.xaml in another window. I am only able to get grayed out titles of these commands. I am wondering what should be should be done in order to get a full access.
Sample of the command:
public partial class MainWindow : Window
{
public static RoutedUICommand AddCommand1 = new RoutedUICommand("Command ", "command1", typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(new CommandBinding(AddCommand1, AddCommand1Executed));
}
private void AddCommand1Executed(object sender, ExecutedRoutedEventArgs e)
{
AddNewItem picker = new AddNewItem();
picker.ShowDialog();
}
I access these command in style through databinding:
<Menu x:Name="TaskMenuContainer"><MenuItem x:Name="menuItem" Header="TASKS" ItemsSource="{Binding}" Template="{DynamicResource TaskMenuTopLevelHeaderTemplateKey}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Text, RelativeSource={RelativeSource Self}}" />
<Setter Property="CommandTarget" Value="{Binding RelativeSource={RelativeSource Self}}"/>
</Style>
</MenuItem.ItemContainerStyle>
These commands work in pages that is loaded inside MainWindow.xaml through frame. However, if I have pop up window that is not part of MainWindow.xaml these commands are only grayed out and not functional anymore (cannot be executed). Any advice is highly appreciated!
The way you define the command, you define it for a particular window. If you want to handle the command globally, at the application level, you can use CommandManager.RegisterClassCommandBinding:
First, define you command in a separate static class:
public static class GlobalCommands
{
public static RoutedUICommand AddCommand1 = new RoutedUICommand("Command ", "command1", typeof(MainWindow));
}
Then, in you window or whatever place you want to put the command logic, register the command handlers:
public partial class MainWindow : Window
{
static MainWindow()
{
CommandManager.RegisterClassCommandBinding(typeof(Window), new CommandBinding(GlobalCommands.AddCommand1, AddCommand1Executed, CanAddExecute));
}
private static void AddCommand1Executed(object sender, ExecutedRoutedEventArgs e)
{
AddNewItem picker = new AddNewItem();
picker.ShowDialog();
}
}
And in your menu style you should change the binding to x:Static:
<Setter Property="Command" Value="{x:Static my:GlobalCommands.AddCommand1}" />
When the command is routed, when checking for command bindings in each active element in the UI, the bindings registered for each element's class will also be checked. By registering the binding here, you can cause every instance of a class to be able to handle the specific command.
So, in the above example, the type Window is used and this will cause the routing to find the command binding in any instance of Window, once the routing reaches that instance in its search for a command binding.
You could instead, for example, restrict the handling to a specific subclass of Window, so that the command will only be bound in an instance of that subclass. Or you can use some other UI element type, so that that the presence of that specific type of element will cause the event to be handled. Just set the owning type for the registered command binding appropriately for your specific needs.
I have a ClockFace UserControl that exposes a number of properties to enable users to style it. The clock has two Ellipse objects as borders; an outer border and an inner border.
<Ellipse Name="OuterBorder" Panel.ZIndex="5" StrokeThickness="{Binding BorderOuterThickness}" Stroke="{Binding BorderOuteBrush}" />
<Ellipse Name="InnerBorder" Panel.ZIndex="6" StrokeThickness="{Binding BorderInnerThickness}" Margin="{Binding StrokeThickness, ElementName=OuterBorder}" Stroke="{Binding BorderInnerBrush}">
public static readonly DependencyProperty BorderInnerBrushProperty = DependencyProperty.Register("BorderInnerBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(new LinearGradientBrush(Color.FromRgb(118, 57, 57), Color.FromRgb(226, 185, 185), new Point(0.5, 0), new Point(0.5, 1))));
public Brush BorderInnerBrush
{
get { return (Brush)GetValue(BorderInnerBrushProperty); }
set { SetValue(BorderInnerBrushProperty, value); }
}
public static readonly DependencyProperty BorderOuterBrushProperty = DependencyProperty.Register("BorderOuterBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(new LinearGradientBrush(Color.FromRgb(226, 185, 185), Color.FromRgb(118, 57, 57), new Point(0.5, 0), new Point(0.5, 1))));
public Brush BorderOuterBrush
{
get { return (Brush)GetValue(BorderOuterBrushProperty); }
set { SetValue(BorderOuterBrushProperty, value); }
}
These can be set in a style and will update correctly when the styles are switched. I thought that I would be clever and add a shortcut property called BorderBrush that passes itself to the BorderOuterBrush property and then passes a copy of itself with its gradient reversed to the BorderInnerBrush property. To enable this code to run when the property was set after initialisation (by switching styles), I had to implement a PropertyChangedCallback method that calls the SetBorderBrushes method.
public new static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(OnBorderBrushChanged));
private static void OnBorderBrushChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
((ClockFace)dependencyObject).SetBorderBrushes((Brush)e.NewValue);
}
public new Brush BorderBrush
{
get { return (Brush)GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
}
private void SetBorderBrushes(Brush brush)
{
if (brush != null)
{
BorderOuterBrush = brush;
Brush innerBrush = BorderOuterBrush.Clone();
if (brush.GetType() == typeof(LinearGradientBrush) || brush.GetType() == typeof(RadialGradientBrush))
{
foreach (GradientStop gradientStop in ((GradientBrush)innerBrush).GradientStops)
{
gradientStop.Offset = 1 - gradientStop.Offset;
}
}
BorderInnerBrush = innerBrush;
}
}
When the BorderBrush property is set in a style, it all works just fine... that is until I switch the style at run time. Now the strangest thing happens... let me explain.
I have four preset styles and individually, they all work just fine. Three of them use the two BorderInnerBrush and BorderOuterBrush properties and one uses the shortcut BorderBrush property. I can switch between the styles using a ContextMenu and some code behind that accesses the xaml styles from Resources and sets them to the ClockFace object's Style property.
I can switch between the three styles that don't use the shortcut property endlessly without problem. I can also switch to the style that does use the shortcut property and it appears just fine. This is when the strangeness begins.
After switching to the style that uses the BorderBrush property, the BorderInnerBrush and BorderOuterBrush properties simply stop working. The preset Brush objects set in the various styles no longer get set on the two Ellipse objects. I pluged in some PropertyChangedCallback methods to the inner and outer border properties to see what was going on.
When I first run the application, I can switch to the three styles that don't use the shortcut property without problem. I put breakpoints on all three border Brush properties' PropertyChangedCallback methods and debugged the program. When switching to each of these three styles, the inner and outer border properties' callback methods' breakpoints were hit as you would expect. When switching to the style that uses the BorderBrush property, its callback method's breakpoint was hit. The SetBorderBrushes method sets the two other border Brush objects so the inner and outer border properties' callback methods' breakpoints were then hit as you would expect.
Again, this is the weird part. When switching to any of the other three styles after this, the inner and outer border properties' callback methods' breakpoints are no longer hit at all. Instead, the breakpoint in the callback method attached to the BorderBrush property is hit an the value of e.NewValue is null. As null values are ignored in the SetBorderBrushes method, no further breakpoints are hit.
After further investigation, I discovered that the e.NewValue was null because there was no default value set on the BorderBrushProperty DependencyProperty. Indeed, after adding a default Brush object to the declaration, this is the Brush that would be passed in e.NewValue in the callback method. Although the breakpoints in the two inner and outer border properties' callback methods would get hit after this, it was only because they are set in the SetBorderBrushes method. The Brush objects set in the BorderInnerBrush and BorderOuterBrush objects in the styles never get passed to these properties after the BorderBrush property has been used once.
One final point to note is that just as the default value of the BorderBrushProperty property is set when a value is not explicitly set in a style, the default values of the inner and outer border DependencyProperty objects are also set when their values are not explicitly set in a style, but only when the BorderBrush property is not also set in the style.
I've been stuck on this for days now and although an easy solution would be to remove the shortcut property, I'd rather find out what's going on and fix it. I hope I've provided enough information for some bright spark to be able to solve this problem, so, if you have any ideas or questions, please share them. Many thanks.
UPDATE >>
Following Rick's suggestion, I created another pair of Brush properties and set them from the callback methods attached to the other three Brush properties. Using this setup, I could switch between all of the styles without any visual problems... or so I thought.
I can now switch from the style that uses the BorderBrush (shortcut) property to the styles that set the two BorderInner and BorderOuter properties and the borders update correctly, so thanks Rick for getting me one step closer. I still have a problem when switching from the style that sets the BorderBrush property to the style that does not explicitly set any border Brush.
Therefore, I still have the same problem that the BorderInnerBrush and BorderOuterBrush properties don't work directly after being set from code behind. What's new is that if I then switch to a style that sets the two Brush properties, this seems to 'wake them up' again... if I then switch to the style that sets no border Brush, the inner and outer properties set their default values correctly again.
It's only after using a style that sets the two border properties from code behind that they become dead to being set in styles or using their default values. This is so strange... can anyone work it out?
UPDATE 2 >>>
First, thanks so much for your time and example Rick. After copying and pasting Rick's example code into a new project, I had to make a few alterations before it would run... maybe because I don't have Expression Blend 4? I installed the SDK and added the references to the noted dlls, but had to use the following XML namespace declarations instead:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
I also had to change each ChangePropertyAction declaration slightly to:
<ei:ChangePropertyAction TargetName="clock" PropertyName="Style" Value="{StaticResource styleBrush}"/>
Then, it worked fine for me and is a good representation of that portion of my actual ClockFace control, with two exceptions. The first is that my styles are switched from code behind via an event handler - this is clearly not causing my problem. The second difference is that I have set default Brush values on my versions of ActualOuterBrush and ActualInnerBrush, so that users of the control don't have to supply border brushes.
So my final problem is that the default inner and outer border brushes do not get set in a Style with no border properties explicitly set after the BorderBrush property has been set in a Style. Remember, the default values do get set up until this point. So, I experimented with Rick's example and added some default values:
private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Teal));
private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Salmon));
Now this project has a similar problem... the default values are never reset. I'm so baffled by this behaviour.
I've spent so long stuck on this problem now and although I have learned a number of useful things from Rick, I still have this problem with the default values. I've decided that the simplest thing to do is to remove the shortcut BorderBrush property. Rick has helped so much that I am awarding his answer as the correct answer, but if anyone reading this can help with the default value, I'd be grateful to hear from them.
Once a property has been set, styles no longer will be applied. By setting the inner and outer brush properties in your changed handler yourself, the dependency property subsystem is not aware that the the change was actually due to a style versus you doing it explicitly.
One solution is to expose protected read-only ActualInnerBorderBrush and ActualOuterBorderBrush properties and then have all three of the user-definable properties set these actual values in their respective change handlers. This way the user-visible properties can always be "as set by the user" without interfering with each other.
Edit:
Here is a complete working implementation of the five properties:
public class Clock : StackPanel
{
public Brush Brush
{
get { return (Brush)GetValue(BrushProperty); }
set { SetValue(BrushProperty, value); }
}
public static readonly DependencyProperty BrushProperty =
DependencyProperty.Register("Brush", typeof(Brush), typeof(Clock),
new UIPropertyMetadata((d, e) => (d as Clock).OnBrushChanged(d, e)));
public void OnBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ActualInnerBrush = e.NewValue as Brush;
ActualOuterBrush = e.NewValue as Brush;
}
public Brush InnerBrush
{
get { return (Brush)GetValue(InnerBrushProperty); }
set { SetValue(InnerBrushProperty, value); }
}
public static readonly DependencyProperty InnerBrushProperty =
DependencyProperty.Register("InnerBrush", typeof(Brush), typeof(Clock),
new UIPropertyMetadata((d, e) => (d as Clock).OnInnerBrushChanged(d, e)));
public void OnInnerBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ActualInnerBrush = e.NewValue as Brush;
}
public Brush OuterBrush
{
get { return (Brush)GetValue(OuterBrushProperty); }
set { SetValue(OuterBrushProperty, value); }
}
public static readonly DependencyProperty OuterBrushProperty =
DependencyProperty.Register("OuterBrush", typeof(Brush), typeof(Clock),
new UIPropertyMetadata((d, e) => (d as Clock).OnOuterBrushChanged(d, e)));
public void OnOuterBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ActualOuterBrush = e.NewValue as Brush;
}
public Brush ActualInnerBrush
{
get { return (Brush)GetValue(ActualInnerBrushProperty); }
private set { SetValue(ActualInnerBrushPropertyKey, value); }
}
private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey =
DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata());
public static readonly DependencyProperty ActualInnerBrushProperty = ActualInnerBrushPropertyKey.DependencyProperty;
public Brush ActualOuterBrush
{
get { return (Brush)GetValue(ActualOuterBrushProperty); }
private set { SetValue(ActualOuterBrushPropertyKey, value); }
}
private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey =
DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata());
public static readonly DependencyProperty ActualOuterBrushProperty = ActualOuterBrushPropertyKey.DependencyProperty;
}
and here is a little test program to prove that it works:
<Grid>
<Grid.Resources>
<Style x:Key="styleBrush" TargetType="local:Clock">
<Setter Property="Brush" Value="Red"/>
</Style>
<Style x:Key="styleInnerOnly" TargetType="local:Clock">
<Setter Property="InnerBrush" Value="Green"/>
</Style>
<Style x:Key="styleInnerOuter" TargetType="local:Clock">
<Setter Property="InnerBrush" Value="Blue"/>
<Setter Property="OuterBrush" Value="Yellow"/>
</Style>
<Style x:Key="styleEmpty" TargetType="local:Clock"/>
</Grid.Resources>
<StackPanel>
<local:Clock x:Name="clock" Orientation="Horizontal">
<Rectangle Width="100" Height="100" Fill="{Binding ActualInnerBrush, ElementName=clock}"/>
<Rectangle Width="100" Height="100" Fill="{Binding ActualOuterBrush, ElementName=clock}"/>
</local:Clock>
<StackPanel Orientation="Horizontal">
<Button Content="Default">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{x:Null}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="BrushOnly">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleBrush}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="InnerOnly">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleInnerOnly}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="InnerOuter">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleInnerOuter}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="Empty">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleEmpty}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</StackPanel>
</Grid>
This example uses behaviors. If you are not familiar with behaviors, install the Expression Blend 4 SDK and add these namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
and add System.Windows.Interactivity and Microsoft.Expression.Interactions to your project.
I need to set the font family for the next text to be written in a RichTextBox.
I tried setting that with...
<RichTextBox x:Name="RichTextEditor" MaxWidth="1000" SpellCheck.IsEnabled="True"
FontFamily="{Binding ElementName=TextFontComboBox, Path=SelectedItem}"
FontSize="{Binding ElementName=TextSizeComboBox, Path=SelectedValue}"
Width="Auto" Height="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" />
...but it changed the whole text. I suppose that with the Selection property I can restrict the change to be applied just to the selected area. But how for the next -not yet typed- text?
In order to set the FontFamily based on the cursor position you need to define a custom control with a dependency property that helps insert a new Run section by overriding the OnTextInput method.
I included most of the code, you'll need to modify the namespaces to fit your development environment.
The code uses a ViewModel to manage the available fonts and manage if the font changed.
This code is only a prototype and does not deal with focusing issues between the two controls.
To use this code:
1- Type some text in the RichTectBox.
2- Change the font in the ComboBox.
3- Tab back to the RichTextBox.
4- Type some more text.
Here is the custom RichTextBox control:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace RichTextboxFont.Views
{
public class RichTextBoxCustom : RichTextBox
{
public static readonly DependencyProperty CurrentFontFamilyProperty =
DependencyProperty.Register("CurrentFontFamily",
typeof(FontFamily), typeof
(RichTextBoxCustom),
new FrameworkPropertyMetadata(new FontFamily("Tahoma"),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnCurrentFontChanged)));
public FontFamily CurrentFontFamily
{
get
{
return (FontFamily)GetValue(CurrentFontFamilyProperty);
}
set
{
SetValue(CurrentFontFamilyProperty, value);
}
}
private static void OnCurrentFontChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{}
protected override void OnTextInput(TextCompositionEventArgs e)
{
ViewModels.MainViewModel mwvm = this.DataContext as ViewModels.MainViewModel;
if ((mwvm != null) && (mwvm.FontChanged))
{
TextPointer textPointer = this.CaretPosition.GetInsertionPosition(LogicalDirection.Forward);
Run run = new Run(e.Text, textPointer);
run.FontFamily = this.CurrentFontFamily;
this.CaretPosition = run.ElementEnd;
mwvm.FontChanged = false;
}
else
{
base.OnTextInput(e);
}
}
}
}
Here is the XAML:
<Window x:Class="RichTextboxFont.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RichTextboxFont.Views"
xmlns:ViewModels="clr-namespace:RichTextboxFont.ViewModels"
Title="Main Window"
Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding Path=Fonts}"
SelectedItem="{Binding Path=SelectedFont, Mode=TwoWay}"/>
<local:RichTextBoxCustom Grid.Row="1"
CurrentFontFamily="{Binding Path=SelectedFont, Mode=TwoWay}"
FontSize="30"/>
</Grid>
</DockPanel>
</Window>
Here is the ViewModel:
If you do not use view models, let me know and I'll add the base class code too; otherwise, google/stackoverflow can help you too.
using System.Collections.ObjectModel;
using System.Windows.Media;
namespace RichTextboxFont.ViewModels
{
public class MainViewModel : ViewModelBase
{
#region Constructor
public MainViewModel()
{
FontFamily f1 = new FontFamily("Georgia");
_fonts.Add(f1);
FontFamily f2 = new FontFamily("Tahoma");
_fonts.Add(f2);
}
private ObservableCollection<FontFamily> _fonts = new ObservableCollection<FontFamily>();
public ObservableCollection<FontFamily> Fonts
{
get
{
return _fonts;
}
set
{
_fonts = value;
OnPropertyChanged("Fonts");
}
}
private FontFamily _selectedFont = new FontFamily("Tahoma");
public FontFamily SelectedFont
{
get
{
return _selectedFont;
}
set
{
_selectedFont = value;
FontChanged = true;
OnPropertyChanged("SelectedFont");
}
}
private bool _fontChanged = false;
public bool FontChanged
{
get
{
return _fontChanged;
}
set
{
_fontChanged = value;
OnPropertyChanged("FontChanged");
}
}
#endregion
}
}
Here is the Window code-behind where I initialise the ViewModel:
using System.Windows;
namespace RichTextboxFont.Views
{
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.DataContext = new ViewModels.MainViewModel();
}
}
}
There's a much easier way to do this: Implement a toolbar for your RichTextBox.
Unlike WinForms, the RichTextBox in WPF doesn't come with a toolbar by default, but it's really easy to create one yourself. The RichTextBox automatically handles many EditingCommands, so it's just a matter of creating a toolbar and some buttons. Microsoft has provided sample code for this at the bottom of the RichTextBox Overview on MSDN.
Unfortunately, those editing commands don't include setting the FontFace property of the selection, though you can create a ComboBox on the toolbar that can trigger the change with an event handler in the codebehind file.
That's the approach taken in this CodePlex article by Gregor Pross: WPF RichTextEditor
The project is commented in German, but the source itself is very clearly written. The codebehind used for his font selector ComboBox looks like this:
private void Fonttype_DropDownClosed(object sender, EventArgs e)
{
string fontName = (string)Fonttype.SelectedItem;
if (fontName != null)
{
RichTextControl.Selection.ApplyPropertyValue(System.Windows.Controls.RichTextBox.FontFamilyProperty, fontName);
RichTextControl.Focus();
}
}
The main reason that people struggle with the FontFace selection is that after the font selection has been made, you must return focus to the RichTextBox. If the user must manually press tab or click into the RichTextBox, a new text selection gets created and you lose the formatting options you've chosen.
One of the answers to this StackOverflow question discusses that problem.
WPF Richtextbox FontFace/FontSize
This isn't exactly a trivial answer.
To do inline text formatting in a Rich TextBox like you want you will have to modify the Document property of the RichTextBox. Very simply, something like this will work
<RichTextBox >
<RichTextBox.Document>
<FlowDocument>
<Paragraph>
<Run>Something</Run>
<Run FontWeight="Bold">Something Else</Run>
</Paragraph>
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
I think you could create a custom Control that creates a new block element and sets the font properties you need based on the user input.
For example, If the user types something then presses bold. You would want to wrap the previous text in a run and create a new run element setting the FontWeight to bold then the subsequent text will be wrapped in the bolded run.
Again, not a trivial solution but I can't think of any other way to accomplish what you are after.