Force databinding to initialize - wpf

Is there any way that one can force databindings to be initialized on controls right after they are created?
My problem is that I've created a own UserControl derived control which must do some time consuming processing before it is shown. More exactly, create thumbnails of video media using the MediaPlayer component of .Net. I'm displaying my control in a custom made MenuItem control.
As it works now, the control gets initialized right before it is displayed (when a select the parent MenuItem), which starts the time consuming work and forcing me to display some kind of "processing item" information until the control has completed the work.
I need to find a way to make the databinding of filenames to execute as soon as the main window is shown instead of right before my control is displayed. Is it possible?
I've created a small app to demonstrate my problem:
Window1.xaml
<Window x:Class="TestBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style x:Key="MyMenuStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding MenuHeader}"/>
</Style>
</Window.Resources>
<Grid>
<Menu>
<MenuItem Header="Data">
<MenuItem Header="Submenus" ItemsSource="{Binding SubMenus}" ItemContainerStyle="{StaticResource MyMenuStyle}" />
</MenuItem>
</Menu>
</Grid>
</Window>
Window1.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
namespace TestBinding
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new BindingViewModel();
}
}
class BindingViewModel
{
public ObservableCollection<MyMenuItems> SubMenus { get; set; }
public BindingViewModel()
{
SubMenus = new ObservableCollection<MyMenuItems>();
SubMenus.Add(new MyMenuItems("Menu 1"));
SubMenus.Add(new MyMenuItems("Menu 2"));
SubMenus.Add(new MyMenuItems("Menu 3"));
}
}
public class MyMenuItems
{
private string _menuHeader;
public string MenuHeader
{
get
{
return _menuHeader;
}
set
{
_menuHeader = value;
}
}
public MyMenuItems(string header)
{
_menuHeader = header;
}
}
}
If you run this program and set a breakpoint on the line
return _menuHeader; you will notice that this executes as you select the parent menu item.
I would like the program to complete the bindings of the sub menu items as soon as possible after the main window is shown giving the program some time to process the values given by the binding property.

Have you tried PriorityBinding?
<PriorityBinding>
<Binding Path="SlowestProperty" IsAsync="True" />
<Binding Path="MediumSpeedProperty" IsAsync="True" />
<Binding Path="FastestProperty" />
</PriorityBinding>
The Bindings are processed from top to bottom, so be sure to set the IsAsync property (to run those asynchronously) - otherwise, it will just hang on the first one until complete, then on the second one, and finally the third one, freezing the UI in the process. Put the bindings in slowest to fastest order (top to bottom) to kick them off in that order.
As values are returned, higher priority values replace lower priority values (but, not the other way around, should the "slowest" one return first).

So what I understand the problem as is (in your example) the MenuHeader get property would have to calculate something that would take a few seconds, subsequently returning after it finishes the calculation?
If that is the case, what you could do is do the complicated calculation in the constructor of MyMenuItems and just cache the value for when the MenuHeader property is finally read. This will probably hang up your program at start up, and therefore you should in the constructor spawn a new thread for the loading of the cached property value.

Related

Xaml: design a layout with dynamically visible components

In a mvvm application some areas inside a window (in reality it is a UserControl inside MainWindow) are dynamically displayed according to the user selections.
The changing blocks are inside Stackpanels, I have 4 of them and only one at a time is displayed. This is accomplished binding Visibility to a bool property and using the BooleanToVisibilityConverter.
I put all the alternate StackPanel inside parent control. It works correctly, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
How can I easily create the layout having more controls which share the same window area and are displayed one at a time ?
Setting A Design Time Only Data Context
Developing XAML in the Studio Designer can be greatly simplified by setting the Design-Time Data Context.
One implementation is based on setting a duplicate DataContext which will be ignored during the final compilation.
To implement the switching, add to the ViewModel, a property that will inform the designer whether it can be used in Development Mode or not.
I use an MVVMLight situation for this example, but for this declared instance property IsInDesignMode and static property ViewModelBase.IsInDesignModeStatic.
Example:
using System.ComponentModel;
namespace DataContextDesignTime.Example
{
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _flag;
public bool Flag
{
get => _flag;
set
{
if (!Equals(_flag, value))
{
_flag = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Flag)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotFlag)));
}
}
}
public bool NotFlag => !Flag;
}
}
<Window x:Class="DataContextDesignTime.Example.ExamleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataContextDesignTime.Example"
mc:Ignorable="d"
Title="ExamleWindow" Height="450" Width="800">
<d:Window.DataContext>
<local:MyViewModel Flag="True" NotFlag="True"/>
</d:Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<Border Background="LightBlue" Height="200"
Visibility="{Binding Flag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Border Background="LightGreen" Height="400"
Visibility="{Binding NotFlag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
In this example, you can change property values in XAML or in the Property Browser.
And you will immediately see the work of your bindings, triggers, how the display for certain data changes.
Note
This may fail on more complex VMs/packages, but in general by setting the DataContext at design time is not difficult.
I need to recompile the project to see the changes in the properties.
The XAML Designer panel has an «Enable/Disable Project Code» button.
, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
This problem is easily resolved by bringing up the Document Outline tab in visual studio. Once open, navigate to the visible tree and toggle the eyeball to visibly hide/unhide the control[s] one is not interested in; during design time only.

WPF page binding failing

I was hoping someone would be kind enough to steer me in the right direction.
I’m trying to get my head around WPF.
I’ve created a 4.8 framework application with two buttons and a frame on the main window. I’m using Caliburn.Micro for binding – This is my test application to try and understand it.
When you press the first button, the window context is set to a page, with an Employees data grid upon it. The data is fed from a back end Oracle Database (I’m trying to follow the MVVM pattern.) This works perfectly.
When you press the other button, the main window displays a second page within the frame – This time, Departments. This much works. I have a an unbound textbox in the page and this shows as it should.
The part I have spent two days trying to get to work is the data grid for the Departments. It returns blank.
I know for certain that the Departments variable is being populated. I have confirmed this two ways. I have stepped through the code and can see values being populated. I have also placed the code below in the Employee page and had the data grid display with values.
I have also tried to display the employees data grid within the Departments page but it too is blank at run time.
To the best of my ability to discern, the code behind the two pages is identical aside from the obvious name change.
This is from my Departments View Model:
using Caliburn.Micro;
using DataLibrary;
namespace WpfEmployees.ViewModels
{
public class DepartmentsViewModel : Screen
{
public BindableCollection<DepartmentsModel> Departments { get; set; }
public DepartmentsViewModel()
{
DepartmentsProcessor dp = new DepartmentsProcessor();
Departments = new BindableCollection<DepartmentsModel>(dp.LoadDepartments());
}
}
}
From the view:
<DataGrid x:Name="Departments" ></DataGrid>
I'm not seeing any error messages, just a blank data grid.
Any suggestions of where I might look for my error would be very much appreciated.
To get this to work:
Main window:
<Window x:Class="WpfEmployees.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfEmployees"
xmlns:viewmodels="clr-namespace:WpfEmployees.ViewModels"
xmlns:views="clr-namespace:WpfEmployees.Views"
mc:Ignorable="d"
Title="MainWindow" Height="850" Width="650" Loaded="ShowDepartmentsPage">
<Window.Resources>
<DataTemplate x:Name="employeesViewTemplate" DataType="{x:Type viewmodels:EmployeesViewModel}">
<views:EmployeesView DataContext="{Binding}"/>
</DataTemplate
<DataTemplate x:Name="departmentsViewTemplate" DataType="{x:Type viewmodels:DepartmentsViewModel}">
<views:DepartmentsView DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
...
<Button x:Name="ShowEmployeesBtn" Content="Employees"
Margin="10 0 0 0" Click="ShowEmployeesPage"/>
<Button x:Name="ShowDepartmentsBtn" Content="Departments"
Margin="10 0 0 0" Click="ShowDepartmentsPage" />
...
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Main window class button code
public partial class MainWindow
{
...
private void ShowEmployeesPage(object sender, RoutedEventArgs e)
{
DataContext = new EmployeesViewModel();
}
View Model Code
public class EmployeesViewModel
...
public ObservableCollection<EmployeesModel> ObEmployees { get; set; }
...
public EmployeesViewModel()
{
EmployeesProcessor ep = new EmployeesProcessor();
ObEmployees = new ObservableCollection<EmployeesModel>
(ep.LoadEmployees());
}
I don't know if this is the best route. It works but I'm too new at WPF to know. I'm hoping this is what Ed Plunkett had in mind, so it may be. Thank you again, Ed.

TabControl becoming very laggy

We're using a Tabcontrol to display a number of items with rather expensive content, and the issue we're having is that as you iterate over the tabs (selecting them one by one), the responsiveness of the application becomes slower and slower.
This behavior is unexpected as from what I understand, as the selected tab changes, the previously selected tabs content is unloaded first, so that you're only paying the price for one tabs content at a time.
I've managed to simulate the behaviour with the code below. To reproduce :
Run the application
Launch the selected tabs contextmenu (the tabs header contextmenu), it will be responsive
From left to right, go through each tab, selecting one by one
By the time you reach tab ~10, the responsiveness of its contextmenu is now very laggy, as you click a checkbox, its animation takes a few seconds to run through
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<Style TargetType="{x:Type TabItem}"
BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<CheckBox Content="CheckBox" />
<CheckBox Content="CheckBox" />
<CheckBox Content="CheckBox" />
<CheckBox Content="CheckBox" />
<CheckBox Content="CheckBox" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<TabControl Name="tabControl" />
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 25; i++)
{
CreateTab();
}
}
void CreateTab()
{
var itemsControl = new ItemsControl();
for (int i = 0; i < 1000; ++i)
{
itemsControl.Items.Add(new TextBox());
}
tabControl.Items.Add(new TabItem()
{
Header = string.Format("Tab{0}", tabControl.Items.Count),
Content = itemsControl
});
}
}
I am not sure about your complex scenario what you have but for posted sample, issue is not in tabControl but instead in ItemsControl.
ItemsControl by default does not support UI virtualization, you have to make it UI virtualized i.e. whenever TabItem gets loaded, all UI containers to host items will be created i.e. 1000 items will be created.
You can verify that by replacing ItemsControl with ListBox and you can see considerable increase in performance because ListBox by default support UI virtualization and only containers for visible items will be created (may be 100 at a time).
Replace
var itemsControl = new ItemsControl();
with
var itemsControl = new ListBox();
and you will see difference in performance.
In case you want some performance with ItemsControl, you have to make it UI virtualized. Refer to the answer here to make it UI virtualized.
UPDATE
For comment:
The problem is that the application becomes slower and slower as you
select different tabs. This is unexpected. Due to each item being
unloaded before loading a new item and due to each item having the
same content, I'd expect the responsiveness to remain constant.
Yeah you are right that Unloaded event gets called for content of last selected tab item but it only disconnect ItemsControl from Visual Tree. However its containers remains intact and remains in memory. So, with every switch new containers are getting created in memory. That I guess is fair reason for sluggishness of your application.
That you can verify by hooking onto StatusChanged event:
itemsControl.ItemContainerGenerator.StatusChanged += (s, e) => { };
You will see that it gets called twice on every switch to new tabItem but doesn't gets called on switch to already visited tabItem.

WPF Binding spontaneously fails after a short time of use

If one were to compile and run the following code, one would find that selecting and/or deselecting a row causes a line to be written to the Output window (as closer inspection of said code would lead one to believe).
After a short time of changing the selected row of the grid using the arrow keys (holding the Up and Down arrows respectively to traverse the entire data set a few times), one would be shocked (as I was) to notice that Output messages cease, even while continuing to cycle through the grid's rows.
I am attempting to achieve something similar to what was given in this answer.
I am absolutely baffled. What would cause Bindings on my grid to spontaneously fail? Any and all help here would be MUCH appreciated!! Also, should anyone have the time to reproduce this, please comment with your findings.
XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<DataGrid Name="TheGrid">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsSelected"
Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True"
Binding="{Binding Name}" Header="Name"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code-behind:
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows;
namespace WpfApplication1 {
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
TheGrid.ItemsSource = Enumerable.Range(1, 100)
.Select(i => new MyClass("Item " + i));
}
}
public class MyClass : INotifyPropertyChanged {
public string Name { get; private set; }
private bool m_IsSelected;
public bool IsSelected {
get {
return m_IsSelected;
}
set {
if (m_IsSelected != value) {
m_IsSelected = value;
Console.WriteLine(Name + ": " + m_IsSelected);
PropertyChanged(this,
new PropertyChangedEventArgs("IsSelected"));
}
}
}
public MyClass(string name) {
Name = name;
}
public event PropertyChangedEventHandler PropertyChanged =
delegate { };
}
}
Thanks in advance!
EDIT:
Tried applying the DataGridRow
Style using the RowStyleSelector
property - fail.
Tried applying the DataGridRow Style using the Row_Loading and Row_Unloading events - fail.
Tried using a custom MultiSelectCollectionView - fail (didn't work with DataGrid control)
Tried setting VirtualizingStackPanel.IsVirtualizing="False"- fail (unusably slow with hundreds of rows)
Tried messing with VirtualizingStackPanel.VirtualizationMode (Standard or Recycled) - fail.
As stated in one of my comments below, the overarching problem is that I need to bind the SelectedItems property of the DataGrid to my ViewModel, but can't, since SelectedItems is read-only.
There HAS to be some kind of pure-MVVM, out-of-the-box solution for this, but so far, it eludes me!
I just tried this and had the same behavior. I was able to fix the problem by changing the DataGrid to prevent it from virtualizing as follows: <DataGrid Name="TheGrid" AutoGenerateColumns="False" VirtualizingStackPanel.IsVirtualizing="False">.
For more information see this MSDN forum post.

WPF, MVVM, and Menu Foreground Color

I'm new to both WPF and MVVM. I have searched for a good way to dynamically create menus in the MVVM parttern and I am not finding anything to my liking, so I rolled my own solution. It works, but for some reason the Foreground (text) color of the menus are sometimes (just sometimes) not correct.
I added a link for the image below.
http://img220.imageshack.us/img220/1912/badmenu.jpg (Dead Link)
My lowest submenu displays correctly with a white foreground, but its parent menus forground turned to black and is almost impossible to read. If I had hard coded the menus then the parent's forground color would be white. If I move my mouse over the parent its text will switch back to white and the submenu will become black.
Further, once I move my mouse away from the parent, all of its boolean properties IsHighlighted, IsSubmenuOpen, etc... become false, which surprising to me because I would think they should stay true. The end result is I haven't been able to solve this with a style trigger.
Here is my XAML .
<Window.Resources>
<DataTemplate DataType="{x:Type src:ParentMenu}" >
<Menu >
<MenuItem Header="{Binding MenuName}" ItemsSource="{Binding ChildMenuItems}" />
</Menu>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type src:ChildMenu}"
ItemsSource="{Binding ChildMenuItems}" >
<MenuItem Header="{Binding MenuName}" Command="{Binding Path=Command}" />
</HierarchicalDataTemplate>
' StackOverflow is masking my end tag for Window.Resources
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Menus}" />
<Grid>
<!-- Add additional content here -->
</Grid>
</DockPanel>
Both ParentMenu and ChildMenu inherit from a common class that actually holds all the menus and exposes the sub-menus through the ChildMenuItems collection. ChildMenuItems is a list of ChildMenu objects. My ViewModels expose a list of ParentMenu objects.
There are probably better ways to accomplish what I want here. Here is an example:
img132.imageshack.us/img132/4160/bettermenu.jpg (Dead Link)
Any suggestions on what I'm doing wrong and/or how to fix the display problem?
The problem is that your VMs automatically get wrapped in MenuItems, so you essentially have MenuItems nested as the Header of MenuItems.
You can get around this by defining a Style (and pointing to it via ItemContainerStyle) that DataBinds to your VMs (Name to Header, DelegateCommands to Command, etc.) using MenuItem as the DataType.
An example of a way you can do this is below. Note that I've dropped the HierarchicalDataTemplate in favor of an ItemContainerStyle. I also took the liberty of defining a DataTemplate for your MainViewModel as it wasn't very clear how that was data bound.
<Window.Resources>
<DataTemplate DataType="{x:Type src:MainViewModel}">
<ItemsControl ItemsSource="{Binding Menus}"></ItemsControl>
</DataTemplate>
<DataTemplate DataType="{x:Type src:ParentMenu}" >
<Menu>
<MenuItem Header="{Binding Name}"
ItemsSource="{Binding ChildMenuItems}" ItemContainerStyle="{DynamicResource ChildMenuItemStyle}" />
</Menu>
</DataTemplate>
<Style x:Key="ChildMenuItemStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"></Setter>
<Setter Property="ItemsSource" Value="{Binding ChildMenuItems}"></Setter>
</Style>
</Window.Resources>
I've also cut some of the Command binding out for simplicity, but you can add it back in as necessary.
As requested, here are my ViewModels.
ViewModelBase is the standard one created by studio. MainVieModel has got just enough to in it to create the test menus I was using to experiment with.
Basically I am working towards creating a Parent/Child menu classes I can use with a series of apps we sell to a broad collection of clients. I hope to make it where each customer can have a customizable collection of menus based upon their needs and which moudles they've purchased licenses for.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MainViewModel : ViewModelBase
{
public MainViewModel() { MakeMenus(); }
private void MakeMenus()
{
// Makes some dummy menus to test with.
Menus = new ObservableCollection<MyMenuItem>();
ParentMenu parent;
ChildMenu child;
parent = new ParentMenu("First Level");
Menus.Add(parent);
child = new ChildMenu(parent, "second level");
parent.ChildMenuItems.Add(child);
ChildMenu child2 = new ChildMenu(child, "third level");
child2.MenuCommand = new DelegateCommand(CommandTest,
CommandCanExecute_First);
child.ChildMenuItems.Add(child2);
child = new ChildMenu(parent, "second level 2");
parent.ChildMenuItems.Add(child);
child2 = new ChildMenu(child, "third level 2");
child2.MenuCommand = new DelegateCommand(CommandTest,
CommandCanExecute_Second);
child.ChildMenuItems.Add(child2);
parent = new ParentMenu("Another First");
parent.ChildMenuItems.Add(new ChildMenu(parent, "Another Second"));
Menus.Add(parent);
OnPropertyChanged("Menus");
}
private bool ExecuteToggle { get; set; }
private void CommandTest() { ExecuteToggle = !ExecuteToggle; }
public ObservableCollection<MyMenuItem> Menus { get; private set;}
public bool CommandCanExecute_First() { return ExecuteToggle; }
public bool CommandCanExecute_Second() { return !ExecuteToggle; }
}

Resources