Is it possible to add a custom spellchecking dictionary to a style? - wpf

I've found numerous sites that provide examples of how to add a custom spellchecking dictionary to an individual textbox like so:
<TextBox SpellCheck.IsEnabled="True" >
<SpellCheck.CustomDictionaries>
<sys:Uri>customdictionary.lex</sys:Uri>
</SpellCheck.CustomDictionaries>
</TextBox>
And I've tested this in my app and it works just fine.
However, I have industry specific jargon that I need to have ignored across ALL the textboxes in the application and applying this custom dictionary to each one individually seems to spit in the face of styles. At the moment I have a global textbox style to turn on spellchecking:
<Style TargetType="{x:Type TextBox}">
<Setter Property="SpellCheck.IsEnabled" Value="True" />
</Style>
I tried to do something like this to add the custom dictionary, but it doesn't like it, since the SpellCheck.CustomDictionaries is read only and setters only take writable properties.
<Style TargetType="{x:Type TextBox}">
<Setter Property="SpellCheck.IsEnabled" Value="True" />
<Setter Property="SpellCheck.CustomDictionaries">
<Setter.Value>
<sys:Uri>CustomSpellCheckDictionary.lex</sys:Uri>
</Setter.Value>
</Setter>
</Style>
I have done extensive searches looking for the answer to this, but all examples show only the one-use scenario in the specific textbox as cited in the first code block. Any help is appreciated.

I had the same issue and couldn't solve it with a style but created some code that accomplished the job.
First, I created a method to find all the textboxes contained within the visual tree of a parent control.
private static void FindAllChildren<T>(DependencyObject parent, ref List<T> list) where T : DependencyObject
{
//Initialize list if necessary
if (list == null)
list = new List<T>();
T foundChild = null;
int children = VisualTreeHelper.GetChildrenCount(parent);
//Loop through all children in the visual tree of the parent and look for matches
for (int i = 0; i < children; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
foundChild = child as T;
//If a match is found add it to the list
if (foundChild != null)
list.Add(foundChild);
//If this control also has children then search it's children too
if (VisualTreeHelper.GetChildrenCount(child) > 0)
FindAllChildren<T>(child, ref list);
}
}
Then, anytime I open a new tab/window in my application I add a handler to the loaded event.
window.Loaded += (object sender, RoutedEventArgs e) =>
{
List<TextBox> textBoxes = ControlHelper.FindAllChildren<TextBox>((Control)window.Content);
foreach (TextBox tb in textBoxes)
if (tb.SpellCheck.IsEnabled)
Uri uri = new Uri("pack://application:,,,/MyCustom.lex"));
if (!tb.SpellCheck.CustomDictionaries.Contains(uri))
tb.SpellCheck.CustomDictionaries.Add(uri);
};

Related

ElementName bindings not working reliably - suspected dispatcher race condition

Download repro project
Edit
The solution working sometimes completely threw me off my initial wondering why this is working in the first place. After all, the items aren't part of the Visual tree. In the end, it makes total sense:
The buttons in that collection aren't in the visual tree and thus element bindings don't work.
Applying the templates puts them into the visual tree and binding, if applied at this time, start working.
This confirms the suspected race condition.
A colleague of mine did some extended debugging that showed the issue as well - in the cases the binding succeeded, OnApplyBinding was invoked first. So using the collection without adjusting the logical tree was simply flawed.
Thanks for the replies that put back on the right track!
Original Post
I have a view control that exposes an ObservableCollection, My view can contain arbitrary elements, e.g. buttons. Note the ElementName binding on the button:
<local:ViperView>
<local:ViperView.MenuItems>
<Button Content="{Binding ElementName=btn, Path=Content}" />
</local:ViperView.MenuItems>
<Grid>
<Button x:Name="btn" Content="HELLO WORLD" />
</Grid>
</local:ViperView>
The control's ControlTemplate just renders the content using an ItemsControl:
<ControlTemplate ...
...
<ItemsControl
x:Name="PART_NavigationMenuItemsHost"
ItemsSource="{Binding MenuItems, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
The view above is assigned to the ActiveView property of my main view model. The main window just displays the view via data binding.
Now the problem: The ElementName binding within that view doesn't work reliably if the view is not immediately assigned to the view model after it's creation.
ElementName bindings work like this:
MainViewModel.ActiveView = new ViperView();
ElementName bindings works sometimes using normal priority:
var view = new ViperView();
Dispatcher.BeginInvoke(() => MainViewModel.ActiveView view);
ElementName binding always fails if the view model property is set with low priority:
var view = new ViperView();
Dispatcher.BeginInvoke(DispatcherPriority.Render, () => MainViewModel.ActiveView = view);
ElementName binding sometimes works if the property is set from a worker thread (Binding engine marshalls back to the UI thread):
var view = new ViperView();
Task.Factory.StartNew(() => MainViewModel.ActiveView = view);
ElementName binding always fails if the worker thread has a delay:
var view = new ViperView();
var view = new ViperView();
Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
MainViewModel.ActiveView = view;
});
I don't have an answer to this. It appears to be related to timings. For example, if I add a short Thread.Sleep to the Task sample above, this always causes the bindings to break, while without the sleep, it only sometimes breaks.
This is quite the show stopper for me - any pointer are appreciated...
Thanks for your advice
Philipp
As far as I know, ElementName binding is not updated at any time: it'll only bind to the property once and then stop updating.
This could explain your problem here: the first binding will happen (or won't) depending on the timestamp.
There is a change you can fix it by specifying the UpdateSourceTrigger property for the binding:
<Button Content="{Binding ElementName=btn, Path=Content, UpdateSourceTrigger=PropertyChanged}" />
This will make sure your binding gets updated every time btn.Content is updated.
Hope this works =)
I can't quite explain why the first option works. However I can explain why the other ones wont work.
Okay, first of all, ElementName can only work when elements are in the same visual tree. Notice that NavigationButtonItems are seperate from the actual content of ViperView.
Thus say you do:
<Button Content="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=WpfApplication2:ViperView}}" />
(it's part of NavigationButtonItems). Now if the NavigationButtonItems and ViperView items are not blended into one(blending to one visual tree happens in ControlTemplate), then this binding would fail, and STAY as failed.
Now say the visual tree happens to be ONE as the binding is happening, then the binding will succeed and everything is nice.
Note that blending into one visual tree happens when you render the content, eg do:
dc.ActiveScreen = viperview;
Here is a quick example to demonstrate how you can do it little better:
<Button
Background="Purple"
Width="100"
Height="20"
>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding IsReady}" Value="True">
<Setter Property="Content" Value="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=WpfApplication2:ViperView}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
IsReady property should be in viewModel and it essentially tells that "YES, everything is rendered as one visual tree now, you can apply bindings."
If you do the IsReady trick, the ActualWidth will start working:
Task.Factory.StartNew(() =>
{
Thread.Sleep(10);
dc.ActiveScreen = view;
//ps you might need to force wpf finish with rendering here. like use Application.DoEvents()
dc.IsReady = true;//force bindings since everything is one now.
});
Let me know if you need clarifications.
Given that the ElementName binding in part fails before the buttons in the sample are even added to the collection of the parent view, there's not much I can do to intercept the bindings. A slightly dirty workaround would be to just refresh the bindings in those controls once the template has been applied and the visual tree established:
Fron OnApplyTemplate, invoke:
internal static class BindingUtil
{
/// <summary>
/// Recursively resets all ElementName bindings on the submitted object
/// and its children.
/// </summary>
public static void ResetElementNameBindings(this DependencyObject obj)
{
IEnumerable boundProperties = obj.GetDataBoundProperties();
foreach (DependencyProperty dp in boundProperties)
{
Binding binding = BindingOperations.GetBinding(obj, dp);
if (binding != null && !String.IsNullOrEmpty(binding.ElementName)) //binding itself should never be null, but anyway
{
//just updating source and/or target doesn’t do the trick – reset the binding
BindingOperations.ClearBinding(obj, dp);
BindingOperations.SetBinding(obj, dp, binding);
}
}
int count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
//process child items recursively
DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
ResetElementNameBindings(childObject);
}
}
public static IEnumerable GetDataBoundProperties(this DependencyObject element)
{
LocalValueEnumerator lve = element.GetLocalValueEnumerator();
while (lve.MoveNext())
{
LocalValueEntry entry = lve.Current;
if (BindingOperations.IsDataBound(element, entry.Property))
{
yield return entry.Property;
}
}
}
}
Another fix, and probably preferable, would be to change the logical tree at runtime. Adding the code below to my view solves the issue, too:
public class ViperView : ContentControl
{
private readonly ObservableCollection<object> menuItems = new ObservableCollection<object>();
public ObservableCollection<object> NavigationMenuItems
{
get { return menuItems; }
}
public ViperView()
{
NavigationMenuItems.CollectionChanged += OnMenuItemsChanged;
}
private void OnMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (var newItem in e.NewItems)
{
AddLogicalChild(newItem);
}
}
}
protected override IEnumerator LogicalChildren
{
get
{
yield return this.Content;
foreach (var mi in NavigationMenuItems)
{
yield return mi;
}
}
}
}

Design-time-only background color in WPF?

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 Resource­Dict­ionary 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:

Apply custom style to a TransitionFrame in WP7

I want my TransitionFrame's backgound to be white, other then black, as is by default.
I have added the following style declaration on the PhoneApplicationPage:
<Style TargetType="toolkit:TransitionFrame">
<Setter Property="Background" Value="White"/>
</Style>
How do I force the transition frame to use my style, that is defined in each page separately?
In App.xaml.cs, in InitializePhoneApplication:
RootFrame = new TransitionFrame();
RootFrame.Background = new SolidColorBrush(Colors.White);
In the same way, if you have more properties, and want to use a style, you could probably use
RootFrame = new TransitionFrame();
RootFrame.Style = (Style) Current.Resources["MyStyle"];
Edit: Might've misread the question, if you want a custom style per page (I don't judge :p) then you can access the rootframe by adding static to your RootFrame declaration:
public static TransitionFrame RootFrame { get; private set; }
and elsewhere, simply using App.RootFrame.<property>

WPF: How to show a "Please Wait" tab

In my TabControl wrapper control, I want to provide the following function:
void AddTab(Func<object> tabContentGenerator)
The function should add a new TabItem with a “Please wait” content, and then call the tabContentGenerator function to get the object to show and replace that TabItem’s Content with the returned object.
I tried to implement the call to tabContentGenerator in a BackgrounWorker. However, the tabContentGenerator function usually creates a UserControl to be used as the content, and this causes an exception when called in a BackgroundWorker. Do you have another idea on how to achieve the required behavior of having “Please wait” tab item that is later replaced with the real content (that needs to be generated in an STA thread)?
As you've probably figured out you need to construct framework objects on the UI thread, not in the background worker. I assume this is the nub of the question.
This looks like some sort of plug-in framework, where tabContentGenerator is injected. If so, I'd use two actions, one that does the long running work and the other that creates the controls. Your extended TabControl would run the first in DoWork and the second in WorkerCompleted.
For example (pseudo-code):
public void AddTab(Action backgroundAction, Func<FrameworkElement> constructUiAction)
{
var tab = ...
var worker = new BackgroundWorker();
worker.DoWork += (sender, e) => { backgroundAction(); };
worker.RunWorkerCompleted += (sender, e) =>
{
var ui = constructUiAction();
if (ui != null) tab.Content = ui;
};
worker.RunWorkerAsync();
}
The other option is to have the action return a FrameworkElementFactory which it then uses to instantiate the GUI on the UI thread via a ControlTemplate. FrameworkElementFactory is not a DispatcherObject and can be created on a non-GUI thread. It's harder to create the UI from factories, but if the client specifies a control template in a resource in XAML they can get the FrameworkFactoryElement from its visual tree (e.g. ((ControlTemplate)FindResource("MyTemplate")).VisualTree).
public void AddTab(Func<FrameworkElementFactory> tabContentGenerator)
{
var tab = ...
var worker = new BackgroundWorker();
FrameworkElementFactory uiFactory = null;
worker.DoWork += (sender, e) => { uiFactory = tabContentGenerator(); }
worker.RunWorkerCompleted += (sender, e) =>
{
if (uiFactory != null)
{
var template = new ControlTemplate();
template.VisualTree = uiFactory;
template.Seal();
tab.Content = template.LoadContent();
};
}
worker.RunWorkerAsync();
}
You might have a look at http://wpftoolkit.codeplex.com/wikipage?title=Extended%20WPF%20Toolkit%20Controls This is showing an indefinite progress bar.
A simple way to do this is to add a Waiting property to your view model, create a UI element for use when the tab is waiting, and put a style on the content, e.g.:
<Grid>
<DockPanel>
<DockPanel.Style>
<Style TargetType="DockPanel">
<Setter Property="Visibility" Value="Visible"/>
<DataTrigger Binding="{Binding Waiting}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style>
<! -- normal content goes here -->
</DockPanel>
<DockPanel>
<DockPanel.Style>
<Style TargetType="DockPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<DataTrigger Binding="{Binding Waiting}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style>
<! -- waiting content goes here -->
</DockPanel>
</Grid>
The command that launches the BackgroundWorker should update Waiting to true (and have the object raise PropertyChanged) before calling DoWork. The BackgroundWorker's RunWorkerCompleted event handler sets Warning back to false.
You'll note that I'm not using any AddTab method to create my tab. Under most circumstances, you should never be writing code that directly creates WPF objects. It's much, much more productive to do it declaratively. When I was learning WPF, I found myself saying, a lot, "I need to create this in code because I can't do X in XAML." The right answer to that is to learn how to do X in XAML, because you almost certainly can, whatever X is.

Get a WPF ListBox item from MouseLeftButtonDown

I want to run some code when the user single clicks on any given ListBox item. My setup is a ListBox with a custom ItemsPanelTemplate (Pavan's ElementFlow). Based on the position data that comes in to MouseLeftButtonDown is there a way to tell which item is was clicked? This is made a bit more difficult (or more confusing) by the custom ItemsPanelTemplate.
You can have an ItemContainerStyle, and specify an EventSetter in it:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="MouseLeftButtonDown" Handler="ListBoxItem_MouseLeftButtonDown" />
...
Then, in the handler of the MouseLeftButtonDown, the "sender" will be the ListBoxItem.
ALSO, if you don't want to use this method, you can call HitTest to find out the Visual object at a specified position:
HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);
ListBoxItem lbi = FindParent<ListBoxItem>( result.VisualHit );
public static T FindParent<T>(DependencyObject from)
where T : class
{
T result = null;
DependencyObject parent = VisualTreeHelper.GetParent(from);
if (parent is T)
result = parent as T;
else if (parent != null)
result = FindParent<T>(parent);
return result;
}

Resources