WPF: How to show a "Please Wait" tab - wpf

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.

Related

NumericUpDown - Focus not set

I am using NumericUpDown control (from WPFs extended toolkit Version 2.9) and I am trying to set focus on it via attached property.
My XAML
<xctk:DecimalUpDown FormatString="F5"
wbui:FocusExtension.IsFocused="{Binding Path=IsFocusedMenge, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Value="{Binding Path=Menge, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
Here my FocusExtensiion.IsFocues
private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)d;
if (e.OldValue == null)
{
fe.GotFocus += FrameworkElement_GotFocus;
fe.LostFocus += FrameworkElement_LostFocus;
}
if (!fe.IsVisible)
{
fe.IsVisibleChanged += new DependencyPropertyChangedEventHandler(FrameworkElement_IsVisibleChanged);
}
if ((bool)e.NewValue)
{
fe.Focus(); // will be called
}
}
When i set my Property IsFocusedMenge to true, then the focus will not be set. The code line fe.Focus() will be called when I set a Breakpoint there.
I found another topic here (How to set focus on NumericUpDown control?), but when I set this attribute Focusable=true, I will get an StackOverFlowException when calling the fe.Focus() method.
Any ideas? Thx.
Update
Also tried it with adding an event to the grid, to set the focus in the View/UserControl ... but without success.
private void GridMenge_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
this.Menge.Focus();
}
}
Focus is still not set (Property Focusable set to True/False - no changes)
I got some information from the official support.
This is already fixed. The fix is included in v3.1.
(You can see the discussion thread here: https://wpftoolkit.codeplex.com/discussions/658785)
To fix it, go in file:
Xceed.wpf.Toolkit/NumericUpDown/Themes/Aero2.NormalColor.xaml
(for Windows8 and up)
Xceed.wpf.Toolkit/NumericUpDown/Themes/Generic.xaml
(for other Windows)
In the style for "NumericUpDown"
a) Replace
<Setter Property="Focusable" Value="False" />
with
<Setter Property="IsTabStop" Value="False" />
b) In the "PART_TextBox"
replace
IsTabStop="{TemplateBinding IsTabStop}"
with
IsTabStop="True"

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;
}
}
}
}

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

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);
};

Event trigger for animation in Prism

I want to trigger an animation whenever a property is set (say "StatusMessages") in my ViewModel. The developer has already had the event set up:
eventAggregator.GetEvent<ShowStatusEvent>().Subscribe(
(message) => ShowStatus(message), ThreadOption.UIThread
);
private void ShowStatus(MyApp.MyModelViews.StatusMessage statusMessage){
// set some values in status message view model
}
What I don't know is what would be the Prism way of hooking up the event with my View (preferably in XAML) so that it triggers the animation. The following "DataTrigger" only works (i.e., triggers the animation) when the source is evaluated to a certain value, say changed from "Debug" to "Error". So if the value is set 12 times but to the same value, say "Debug" each time, the datatrigger only happens at most one time (from default to the new value, assuming they are not equal).
<DataTrigger Binding="{Binding DataContext.StatusMessages, ElementName=MyPanel}" Value="Error">...</DataTrigger>
The workaround now we came up with is to have a new bool property introduced especially for triggering the animation:
public bool CanBeginStoryboard{
get
{
return canBeginStoryboard;
}
set
{
canBeginStoryboard = value;
RaisePropertyChanged(() => CanBeginStoryboard);
}
}
private void ShowStatus(MyApp.MyModelViews.StatusMessage statusMessage)
{
CanBeginStoryboard = false;
//// set some values in status message view model
CanBeginStoryboard = true;
}
<DataTrigger Binding="{Binding DataContext.CanBeginStoryboard, ElementName=MyPanel}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>...</BeginStoryboard>
</DataTrigger.EnterActions>
I assume there is a standard "Prism's way" of doing such things (like WPF "RoutedEvent">"EventTrigger"?), which is different from what we are doing here? BTW, we are using Prism 4. Thanks in advance.

How to access command from MainWindow level in another Window?

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.

Resources