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.
Related
I am currently transferring my app from WinForms to WPF.
Since I'm new in WPF, I stucked at creating DataTemplates for my treeView items. The screenshot shows how my treeview looked in WinForms version, and I need to get close result in WPF.
(My WinForms treeview)
As you can see, my DataTemplate's logic should take into account these factors:
Node type / defines which icon and fields combination will be displayed for particular item (node). App has about 7-8 node types. Type stored in separate node's field.
Variable values / I need to replace with text if null, etc
Numeric variable values / e.g.: set gray color if zero, etc.
Other properties / e.g.: adding textblocks depending on boolean fields.
And so on...
All these factors result into huge amount of possible item params combinations.
Also I'm using DevComponents WPF DotNetBar AdvTree to divide item properties into columns. I presume I should create 'sub templates' for different field sets and compose from them the entire DataTemplate for each column.
I've learned about triggers, and have to say that implementing my logic with triggers will make my subtemplates huge anyway.
(Current state of my WPF treeview)
So here are my questions:
Are there any ways to dynamically compose complex templates with C# code (without creating raw XAML and loading it at runtime)?
Maybe I should use completely different way (instead of using DataTemplate)? In Winforms I just used OwnerDraw mode, so the task was MUCH easier than in WPF :(
And how to display nested properties inside template? e.g.: Item.Prop.Subprop1.Subprop2.Targetprop.
PS: English is not my first language, sorry for your eyes.
1) The answer is yes.
For exemple if you want to define a template in your window for a simple string
public MainWindow()
{
InitializeComponent();
DataTemplate template = new DataTemplate(typeof(string));
FrameworkElementFactory borderFactory = new FrameworkElementFactory(typeof(Border));
borderFactory.SetValue(Border.PaddingProperty, new Thickness(1));
borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
borderFactory.SetValue(Border.BorderBrushProperty, Brushes.Red);
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
textFactory.SetBinding(TextBlock.TextProperty, new Binding
{
Mode = BindingMode.OneWay
});
borderFactory.AppendChild(textFactory);
template.VisualTree = borderFactory;
myControl.ContentTemplate = template;
}
And in the Window you just put something like
<ContentControl x:Name="myControl" Content="Test text" Margin="10"/>
Your content control will render the string surrounded by a red border.
But as you anc see it is really complex to define your templates in this way.
The only scenario where i could imagine this approache is for some kind of procedurally generated templates.
Another way is to generate a string for the template and then load it with XamlReader:
string xaml = "<Ellipse Name=\"EllipseAdded\" Width=\"300.5\" Height=\"200\"
Fill=\"Red\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>";
object ellipse = XamlReader.Load(xaml);
2) I don't really see the need to generate templates in code behind. For exemple for this kind of data structure:
public class User
{
public string Name { get; set; }
public User Friend { get; set; }
}
public class RootNode
{
public string Title { get; set; }
public User User { get; set; }
public List<Node> Nodes { get; set; }
}
public class Node
{
public string Title { get; set; }
public List<SubNode> SubNodes { get; set; }
}
public class SubNode
{
public string Title { get; set; }
}
You can define this type of template:
<Window
...
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:RootNode}" ItemsSource="{Binding Nodes}">
<StackPanel x:Name="spContainer" Orientation="Horizontal">
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding User.Friend.Friend.Name}"/>
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding User}" Value="{x:Null}">
<Setter TargetName="spContainer" Property="Background" Value="Yellow"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding SubNodes}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:SubNode}" ItemsSource="{Binding Nodes}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding RootNodes}"/>
</Grid>
</Window>
As you can see you can define a template by data type, you can also use triggers to modify the behavior in specific cases, you could also use som binding converters...
3) You can bind to nested properties just like to normal ones :
<TextBlock Text="{Binding User.Friend.Friend.Name}"/>
However in some cases more than two level bindings could fail (fail to resolve or fail to update when property changes, ...)
How do I make this work?
public partial class MyWindow : View<MyViewModel>
{
}
where View is defined as
public class View<T> : Window where T : IViewModel, new()
{
}
XAML:
<local:View
x:Class="Project.MyWindow"
x:TypeArguments="ViewModels:MyViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ViewModels="clr-namespace:Mynamespace.ViewModels;assembly=Mynamespace.ViewModels"
xmlns:local="clr-namespace:Project"
>
I get this error... The name View does not exist in the namespace Project... which of course it does.
and I get this error which really confuses me... The property "TypeArguments" does not exist in the "http://schemas.microsoft.com/winfx/2006/xaml" namespace... which of course it does.
Any clues on how to use generics as a base class for windows in wpf?
The answer to this question is to never use generics with xaml. There is always a better way or a workaround. See the comments on the original post.
EDIT!
Thanks to Micky Duncan (see the comments above) the correct answer to this question can be found by investigating these two links:
http://msdn.microsoft.com/en-us/library/ms741843(v=vs.110).aspx
http://www.codeproject.com/Articles/37317/Generic-Support-In-XAML
This is an Old Topic, but as I came around and others might to:
I don't know since when, but tested in .net 6.0
The approach was right, but without knowing which project settings the questioner used it is hard to say what went wrong.
yes i know it's ugly, but i have to admit is was a prove of concept prototyp
SAMPLE XAML:
<base:ApplicationTabItem x:TypeArguments="entries:ArtikelEntry,vms:ArtikelTabViewModel"
x:Class="S2_Management.DesktopApp.UIElements.AppTabs.ArtikelTab"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:S2_Management.DesktopApp.UIElements.AppTabs"
mc:Ignorable="d"
xmlns:vms ="clr-namespace:S2_Management.DesktopApp.ViewModels"
xmlns:values="clr-namespace:S2_Management.CoreLib.ConstantValues;assembly=S2-Management.CoreLib"
xmlns:entries="clr-namespace:S2_Management.CoreLib.Tables;assembly=S2-Management.CoreLib"
xmlns:base="clr-namespace:S2_Management.DesktopApp.UIElements.Base"
d:DataContext="{d:DesignInstance Type=vms:ArtikelTabViewModel, IsDesignTimeCreatable=true}"
d:DesignHeight="450" d:DesignWidth="800"
>
<TabItem.Header>
<Label Content="{Binding FQ_TabName}"/>
</TabItem.Header>
<Grid KeyUp="OnKeyUp">
<DataGrid ItemsSource="{Binding ItemListe}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" SelectedItem="{Binding SelectedItem}">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<EventSetter Event="MouseDoubleClick" Handler="ItemDoubleClick"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static values:ConstantValueStore.ArtikelTabGridBezeichnungTitle}" Binding="{Binding Artikel_Bezeichnung}" IsReadOnly="True" SortDirection="Ascending" />
<DataGridTextColumn Header="{x:Static values:ConstantValueStore.ArtikelTabGridEinheitenTitle}" Binding="{Binding Einheit.Einheiten_Bezeichnung}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static values:ConstantValueStore.ArtikelTabGridKategorieTitle}" Binding="{Binding ArtikelKategorie.ArtikelKategorien_Bezeichnung}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static values:ConstantValueStore.ArtikelTabGridPreisGruppeTitle}" Binding="{Binding Preisgruppe.Preisgruppen_Bezeichnung}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</base:ApplicationTabItem>
Sample CodeBehind
namespace S2_Management.DesktopApp.UIElements.AppTabs
{
/// <summary>
/// logic for ArtikelTab.xaml
/// </summary>
public partial class ArtikelTab //: ApplicationTabItem<ArtikelEntry, ArtikelTabViewModel>
{
public ArtikelTab()
{
InitializeComponent();
this.DataContext = _vm = getTabViewModel();
}
}
}
Sample BaseClass:
namespace S2_Management.DesktopApp.UIElements.Base
{
public class ApplicationTabItem<ItemType, TVMType> : TabItem, IApplicationTabItem
where TVMType : ITabViewModel<ItemType>, new()
{
protected TVMType? _vm;
protected TVMType getTabViewModel()
{
return new TVMType() { OwnTabItem = this };
}
public virtual RibbonTab? GetRibbonTab()
{
return _vm?.GetRibbonTab();
}
protected void ItemDoubleClick(object sender, MouseButtonEventArgs? e)
{
_vm?.OpenSelectedItemFor(CRUDModus.Read);
}
protected void OnKeyUp(object sender, KeyEventArgs e)
{
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0)
{
switch (e.Key)
{
case Key.D:
_vm?.CopyCommand?.Execute(null);
break;
case Key.N:
_vm?.CreateNewCommand?.Execute(null);
break;
}
}
else
{
switch (e.Key)
{
case Key.Escape:
if (_vm != null) _vm.SelectedItem = default;
break;
case Key.Enter:
ItemDoubleClick(this, null);
break;
case Key.Delete:
case Key.Back:
_vm?.DeleteCommand?.Execute(null);
break;
}
}
}
}
}
Explanation
public class ApplicationTabItem<ItemType, TVMType> : TabItem, IApplicationTabItem
where TVMType : ITabViewModel<ItemType>, new()
This is the superclass, which inherits from TabItem. As we all, should, know:
TabItem
is an UIElement and brings everything we need!
<base:ApplicationTabItem x:TypeArguments="entries:ArtikelEntry,vms:ArtikelTabViewModel"
is what say's hey inherit please!
IMPORTANT:
public partial class ArtikelTab //: ApplicationTabItem<ArtikelEntry, ArtikelTabViewModel>
Original we could inherit in code behind as well, which is :
redundant
Bad style, because you need to change things on two places
but: better and faster readable
but as of today there is an open BUG:
Inheriting from a generic type within a code behind file will fail during the [whatever].xaml.g.s generation. So be VERY sure to remove that inherit from code behind, or at leased put it on comment!!!
Link to MS Blog Page on that topic, but in a very early state of support:
https://learn.microsoft.com/en-us/archive/blogs/mikehillberg/limited-generics-support-in-xaml
TPTB have decided that our app must run in a single window, popping up new windows in modal mode is not allowed.
And naturally, we have a UI design that involves popping up modal dialogs all over the place.
So I added a top-level Grid to the Window. In that Grid I defined no rows or columns, so everything draws in Row 0/Column 0.
The first element in the Grid was another Grid that contained everything that was normally displayed in the Window. The second was a full-sized Border with a gray, semi-transparent Background. The rest were Borders with wide Margins and white Backgrounds, containing the various UserControls that needed to be displayed as popups. All but the first had Visibility="Collapsed".
And then, when I needed to show a popup, I'd set Visibility="Visible" on the gray background and on the appropriate UserControl. The result was a nice shadowbox effect that worked fine.
Until somebody decided that the popups needed to be able to display popups. In a non-predictable order.
The limitation of the method I had implemented, using Visibility="Collapsed" elements in a Grid was that their order was fixed. UserControlB would always be displayed on top of UserControlA, even if it was UserControlB that asked to have UserControlA displayed. And that's not acceptable.
So my next attempt was to define the various UserControls in Window.Resources, and to add them to the Grid in code:
this.masterGrid.Children.Add(this.Resources["userControlA"] as UserControlA);
And that almost works. But the bindings are all messed up.
As an example, one of the controls is supposed to bind a Property to the CurrentItem of a collection in a member object of the Window's viewmodel. When I had the control defined as an invisible item in the Grid, it worked fine. But when I defined it as a Resource, the Property was null - it was never bound.
So I tried binding it in code, after I added it to the grid:
userControlA.SetBinding(UserControlA.myProperty, new Binding()
{ Source = this.viewModel.myCollection.CurrentItem });
And that compiles and runs just fine, but I'm not binding to the right object.
The first time I display the UserControl, I see the right object bound to it. But when I close it, and move the CurrentItem in the collection to a different object, and display the UserControl again, I still see the first object bound. If I close it again, and open it a third time, then I will see the right object bound to the control.
I've checked in code, and the CurrentItem that I'm binding to is right, every time, but it only seems to take every other time.
So I tried explicitly clearing the binding, first:
BindingOperations.ClearBinding(userControlA, UserControlA.myProperty);
userControlA.SetBinding(UserControlA.myProperty, new Binding()
{ Source = this.viewModel.myCollection.CurrentItem });
But that doesn't seem to have made any difference.
In all, it feels like I'm running down a rabbit hole, chasing deeper and deeper into complexity, to solve what should be a fairly simple problem.
Does anyone have any suggestions as to:
How to get binding to work on dynamically-added elements, or
How to get arbitrarily-ordered popups to display, as shadowboxes, without using dynamically-ordered elements?
Thanks in advance.
While it seems really odd for me that you can't create new Windows, I would definitely recommend not to complicate it too much by doing unnecesary things such as storing your views in the MainWindow's resources.
It would be better if you just added new instances of these elements into an ObservableCollection:
XAML:
<Window x:Class="WpfApplication4.Window8"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="Window8" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<StackPanel Background="Green">
<TextBlock Text="This is ViewModel1!!"/>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<StackPanel Background="Blue" HorizontalAlignment="Center">
<TextBlock Text="This is ViewModel2!!"/>
<TextBlock Text="{Binding Text2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel3}">
<StackPanel Background="Red" VerticalAlignment="Center">
<TextBlock Text="This is ViewModel3!!"/>
<TextBlock Text="{Binding Text3}"/>
<TextBox Text="{Binding Text3}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<Button Width="100" Content="Add" Click="Add_Click" DockPanel.Dock="Top"/>
<Button Width="100" Content="Remove" Click="Remove_Click" DockPanel.Dock="Top"/>
<ListBox ItemsSource="{Binding ActiveWidgets}" SelectedItem="{Binding SelectedWidget}">
<ListBox.Template>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</ListBox.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter ContentSource="Content"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DockPanel>
</Window>
Code Behind:
using System.Linq;
using System.Windows;
using System.Collections.ObjectModel;
using System;
namespace WpfApplication4
{
public partial class Window8 : Window
{
private WidgetsViewModel Widgets { get; set; }
public Window8()
{
InitializeComponent();
DataContext = Widgets = new WidgetsViewModel();
}
private Random rnd = new Random();
private int lastrandom;
private void Add_Click(object sender, RoutedEventArgs e)
{
var random = rnd.Next(1, 4);
while (random == lastrandom)
{
random = rnd.Next(1, 4);
}
lastrandom = random;
switch (random)
{
case 1:
Widgets.ActiveWidgets.Add(new ViewModel1() {Text = "This is a Text"});
break;
case 2:
Widgets.ActiveWidgets.Add(new ViewModel2() { Text2 = "This is another Text" });
break;
case 3:
Widgets.ActiveWidgets.Add(new ViewModel3() { Text3 = "This is yet another Text" });
break;
}
Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
}
private void Remove_Click(object sender, RoutedEventArgs e)
{
Widgets.ActiveWidgets.Remove(Widgets.SelectedWidget);
Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
}
}
public class WidgetsViewModel: ViewModelBase
{
public ObservableCollection<ViewModelBase> ActiveWidgets { get; set; }
private ViewModelBase _selectedWidget;
public ViewModelBase SelectedWidget
{
get { return _selectedWidget; }
set
{
_selectedWidget = value;
NotifyPropertyChange(() => SelectedWidget);
}
}
public WidgetsViewModel()
{
ActiveWidgets = new ObservableCollection<ViewModelBase>();
}
}
public class ViewModel1: ViewModelBase
{
public string Text { get; set; }
}
public class ViewModel2: ViewModelBase
{
public string Text2 { get; set; }
}
public class ViewModel3: ViewModelBase
{
public string Text3 { get; set; }
}
}
Just copy and paste my code in a File - New - WPF Application and see the results for yourself.
Since the Grid always places the last UI Element added to it topmost, you will see that Adding items to the observablecollection makes these "different widgets" always appear on top of each other, with the topmost being the last one added.
The bottom line is, when WidgetA requests to open WidgetB, just create a new WidgetBViewModel and add it to the ActiveWidgets collection. Then, when WidgetB is no longer needed, just remove it.
Then, it's just a matter of putting your UserControls inside a proper DataTemplate for each ViewModel. I strongly suggest you keep a separate ViewModel for each of your Widgets, and if you need to share data between them, just share data between the ViewModels.
Don't attempt to do things like ListBox ItemsSource="{Binding Whatever, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}" unless you have a good reason to.
This way you no longer have to deal with Panel.ZIndex stuff. Maybe you can create a couple of attached properties to deal with things like focus and whatnot, but this approach is dead simple, and by far more performant than the Visibility and the Resources approaches.
I am working on WPF, I am designing a control that contains a datagrid, And everything goes well so far.
But something weird just happend: If I show one or more rows in the data grid, the datagrid shows at the end the blank row used to add new rows, as normal, BUT if there is no rows in the data grid, the blank row is not showed either!.
So, I need my datagrid to have always at the end the blank row whether or not data.
I have tried with the properties CanUserAddRows and IsReadOnly but as I said before it only works when there is one or more rows.
Does anybody know how to achieve this?
Thank you in advance.
Do you mean blank row? Have a look at the answers on wpf datagrid blank row missing
This basic set-up does display a blank row (even when bound ItemsSource has no data)
XAML
<Window x:Class="Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Grid>
<DataGrid ItemsSource="{Binding Items}">
<!--Must define columns if you need columns before bound collection has data-->
<DataGrid.Columns>
<DataGridTextColumn Header="Property" Binding="{Binding Path=MyProperty}"> </DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code behind
public class Data
{
public Data(string value) { MyProperty = value; }
public Data() { } // Must have default constructor
public String MyProperty { get; set; }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
List<Data> _items = new List<Data>();
public List<Data> Items
{
get { return _items; }
}
public MainWindow()
{
InitializeComponent();
}
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.