I am an experienced WinForms developer, relatively new to WPF. I have a large WinForms application that uses a couple different base classes to represent dialog boxes. One such example is AbstractOkCancelDialog. That class contains a panel at the bottom of a dialog, with an Ok and Cancel button on the right side of the panel. I'm trying to determine the best way to handle this, as I realize that WPF doesn't provide visual inheritance.
I don't want to have to create OK and Cancel buttons, and place them, for every dialog in the application.
I have read that the way to do this in WPF is with user controls. I can envision creating a user control with OK and Cancel buttons on it. But I don't want to have to manually place that user control on hundreds of dialogs in my application. I'd really like to have something like this:
public AbstractOkCancelDialog = class(Window)
{
protected AbstractOkCancelDialogViewModel _ViewModel;
// AbstractOkCancelDialogViewModel would have commands for OK and Cancel.
// Every dialog would inherit from AbstractOkCancelDialog, and would use
// a viewmodel that inherits from AbstractOkCancelDialogViewModel. In
// this way, all view models would automatically be connected to the OK
// and Cancel commands.
}
I've seen some discussion online about how to create the base class. Those discussions explain how there can't be a xaml file associated with the dialog base class, and I understand that restriction. I just can't figure out how to automatically place the user control with the OK and Cancel buttons.
I'm hoping that someone can point me to a sample solution that shows this kind of structure. Thank you in advance!
Write one dialog class. It's a subclass of Window. It has XAML:
<Window
...blah blah blah...
Title="{Binding Title}"
>
<StackPanel MinWidth="300">
<!-- This is how you place content within content in WPF -->
<ContentControl
Content="{Binding}"
Margin="2"
/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2,20,2,2">
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="OK"
Click="OK_Click"
IsDefault="True"
/>
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="Cancel"
IsCancel="True"
Click="Cancel_Click"
/>
</StackPanel>
</StackPanel>
</Window>
You can fancy that up endlessly, but this is a decent minimum to give you arbitrary content above a row of right-aligned buttons. Adding more buttons as needed could involve either templating that portion of the window as well, or creating them with an ItemsControl (I've done that in our production code), or a few other options.
Usage:
var vm = new SomeDialogViewModel();
var dlg = new MyDialog { DataContext = vm };
For each dialog viewmodel, consumers must define an implicit datatemplate which provides UI for that viewmodel.
I would suggest writing a dialog viewmodel interface which the consumer is expected to implement.
public interface IDialogViewModel
{
String Title { get; set; }
void OnOK();
// Let them "cancel the cancel" if they like.
bool OnCancel();
}
The window can check if its DataContext implements that interface, and act accordingly. If you like, it could require that interface and throw an exception of it isn't implemented, or it could just talk to it only if it's there. If they don't implement it but they still have a Title property, the binding to Title will still work. Bindings are "duck typed".
Naturally, you can write an OKCancelDialogViewModel or a SelectStringFromListViewModel, write corresponding DataTemplates that implement their UIs, and write nice clean static methods which show them:
public static class Dialogs
{
public static TOption Select<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : class
{
// Viewmodel isn't generic because that breaks implicit datatemplating.
// That's OK because XAML uses duck typing anyhow.
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
Options = options
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return vm.SelectedOption as TOption;
}
return null;
}
// We have to call the value-type overload by a different name because overloads can't be
// distinguished when the only distinction is a type constraint.
public static TOption? SelectValue<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : struct
{
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
// Need to box these explicitly
Options = options.Select(opt => (object)opt)
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return (TOption)vm.SelectedOption;
}
return null;
}
}
Here's a viewmodel datatemplate for the above selection dialog:
<Application.Resources>
<DataTemplate DataType="{x:Type local:SelectOptionDialogViewModel}">
<StackPanel>
<TextBlock
TextWrapping="WrapWithOverflow"
Text="{Binding Prompt}"
/>
<ListBox
ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption}"
MouseDoubleClick="ListBox_MouseDoubleClick"
/>
</StackPanel>
</DataTemplate>
</Application.Resources>
App.xaml.cs
private void ListBox_MouseDoubleClick(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
((sender as FrameworkElement).DataContext as IDialogViewModel).DialogResult = true;
}
var a = Dialogs.Select(new String[] { "Bob", "Fred", "Ginger", "Mary Anne" },
"Select a dance partner:");
var b = Dialogs.SelectValue(Enum.GetValues(typeof(Options)).Cast<Options>(),
"Select an enum value:");
Here an example of how to use a custom AlertDialog
UserControl
<UserControl x:Class="Library.Views.AlertMessageDialogView"
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:p="clr-namespace:Library.Properties"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
FlowDirection = "{Binding WindowFlowDirection, Mode=TwoWay}">
<Grid Background="{DynamicResource WindowBackgroundBrush}">
<Canvas HorizontalAlignment="Left" Height="145" VerticalAlignment="Top" Width="385">
<Label HorizontalAlignment="Left" Height="57" VerticalAlignment="Top" Width="365" Canvas.Left="10" Canvas.Top="10" FontSize="14" >
<TextBlock x:Name="txtVocabAnglais" TextWrapping="Wrap" Text="{Binding Message, Mode=TwoWay}" Width="365" Height="57" />
</Label>
<Button x:Name="cmdCancel" Content="{x:Static p:Resources.AlertMessageDialogViewcmdCancel}" Height="30" Canvas.Left="163" Canvas.Top="72" Width="71" Command = "{Binding CancelCommand}" CommandParameter = "null" IsDefault="True"/>
</Canvas>
</Grid>
</UserControl>
ViewModel Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.ComponentModel;
using System.Windows.Controls;
namespace Library.ViewModel
{
public class AlertMessageDialogViewModel : BindableBaseViewModel
{
public event EventHandler CloseWindowEvent;
private string _title;
private string _message;
public BaseCommand<string> YesCommand { get; private set; }
public BaseCommand<string> CancelCommand { get; private set; }
private WinformsNameSpace.FlowDirection _windowFlowDirection;
public AlertMessageDialogViewModel()
{
CancelCommand = new BaseCommand<string>(cmdCancelBtnClick);
WindowFlowDirection = CustomFuncVar.WindowFlowDirection;
}
public WinformsNameSpace.FlowDirection WindowFlowDirection
{
get
{
return _windowFlowDirection;
}
set
{
_windowFlowDirection = value;
OnPropertyChanged("WindowFlowDirection");
}
}
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
}
}
private void cmdCancelBtnClick(string paramerter)
{
if (CloseWindowEvent != null)
CloseWindowEvent(this, null);
}
}
}
DialogMessage Class
using System;
using System.Windows;
using System.Collections.Generic;
namespace Library.Helpers
{
public static class DialogMessage
{
public static void AlertMessage(string message, string title, Window OwnerWindowView)
{
try
{
//Affichage de méssage de succès enregistrement
AlertMessageDialogViewModel alertDialogVM = new AlertMessageDialogViewModel();
alertDialogVM.Message = message;
alertDialogVM.Title = title;
// Auto Generation Window
FrameworkElement view = LpgetCustomUI.AutoGetViewFromName("AlertMessageDialogView");
view.DataContext = alertDialogVM;
Dictionary<string, object> localVarWindowProperty = new Dictionary<string, object>();
localVarWindowProperty = LpgetCustomUI.GetWindowPropretyType_400x145(Properties.Resources.ApplicationTitle);
CummonUIWindowContainer alertDialogView = new CummonUIWindowContainer(view, null, false, localVarWindowProperty);
//End Auto Generation Window
// Attachement de l'évènement de fermture de View au modèle
alertDialogVM.CloseWindowEvent += new EventHandler(alertDialogView.fnCloseWindowEvent);
if (OwnerWindowView!=null)
{
alertDialogView.Owner = OwnerWindowView;
}
else
{
alertDialogView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
alertDialogView.ShowDialog();
}
catch (Exception ex)
{
}
}
}
}
CummonUIWindowContainer Class
namespace CummonUILibrary.CummonUIHelpers
{
public class CummonUIWindowContainer : Window
{
public event RoutedEventHandler CmbRootEvtLanguageChange;
private FrameworkElement currentView;
private ContentControl _contentcontainer;
public CummonUIWindowContainer(string usercontrolName)
{
Contentcontainer = new ContentControl();
currentView = new FrameworkElement();
}
public CummonUIWindowContainer()
{
Contentcontainer = new ContentControl();
currentView = new FrameworkElement();
}
public CummonUIWindowContainer(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
{
Contentcontainer = new ContentControl();
Contentcontainer.Name = "ContentControl";
SetWindowProperty(view, model, setDataContextToView, WindowPropertyList);
}
public void SetWindowProperty(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
{
try
{
LinearGradientBrush brush = new LinearGradientBrush();
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Yellow;
brush.GradientStops.Add(gradientStop1);
GradientStop gradientStop2 = new GradientStop();
gradientStop2.Offset = 0.5;
gradientStop2.Color = Colors.Indigo;
brush.GradientStops.Add(gradientStop2);
GradientStop gradientStop3 = new GradientStop();
gradientStop3.Offset = 1;
gradientStop3.Color = Colors.Yellow;
brush.GradientStops.Add(gradientStop3);
this.Background = brush;
CurrentView = view;
Type elementType = this.GetType();
ICollection<string> WindowPropertyListNames = WindowPropertyList.Keys;
foreach (string propertyName in WindowPropertyListNames)
{
PropertyInfo property = elementType.GetProperty(propertyName);
property.SetValue(this, WindowPropertyList[propertyName]);
}
if (setDataContextToView == true & model != null)
{
CurrentView.DataContext = model;
}
if (CurrentView != null)
{
Contentcontainer.Content = CurrentView;
}
//Contentcontainer.Margin = new Thickness(0,0, 0, 0);
IAddChild container=this;
container.AddChild(Contentcontainer);
}
catch (Exception ex)
{
}
}
public void fnCloseWindowEvent(object sender, EventArgs e)
{
this.Close();
}
public ContentControl Contentcontainer
{
get
{
return _contentcontainer;
}
set
{
_contentcontainer = value;
}
}
public FrameworkElement CurrentView
{
get
{
return currentView;
}
set
{
if (this.currentView != value)
{
currentView = value;
//RaisePropertyChanged("CurrentView");
}
}
}
private void cmbLanguage_SelectionChanged(object sender, RoutedEventArgs e)
{
//CmbRootEvtLanguageChange(sender, e);
}
}
}
How to use the Class
DialogMessage.AlertMessage("My Custom Message", "My Custom Title Message");
Thats how i would do it
Create an abstract base class for your dialog and changing the corresponding ControlTemplate
AbstractOkCancelDialog
public abstract class AbstractOkCancelDialog : Window
{
public static readonly DependencyProperty CancelCommandParameterProperty =
DependencyProperty.Register(
"CancelCommandParameter",
typeof(object),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((object) null));
public static readonly DependencyProperty CancelCommandProperty =
DependencyProperty.Register(
"CancelCommand",
typeof(ICommand),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((ICommand) null));
public static readonly DependencyProperty OkCommandParameterProperty =
DependencyProperty.Register(
"OkCommandParameter",
typeof(object),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((object) null));
public static readonly DependencyProperty OkCommandProperty =
DependencyProperty.Register(
"OkCommand",
typeof(ICommand),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((ICommand) null));
static AbstractOkCancelDialog()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractOkCancelDialog), new
FrameworkPropertyMetadata(typeof(AbstractOkCancelDialog)));
}
public ICommand CancelCommand
{
get => (ICommand) GetValue(CancelCommandProperty);
set => SetValue(CancelCommandProperty, value);
}
public object CancelCommandParameter
{
get => GetValue(CancelCommandParameterProperty);
set => SetValue(CancelCommandParameterProperty, value);
}
public ICommand OkCommand
{
get => (ICommand) GetValue(OkCommandProperty);
set => SetValue(OkCommandProperty, value);
}
public object OkCommandParameter
{
get => GetValue(OkCommandParameterProperty);
set => SetValue(OkCommandParameterProperty, value);
}
}
Style
Put in Generic.xaml[?]
<Style
BasedOn="{StaticResource {x:Type Window}}"
TargetType="{x:Type local:AbstractOkCancelDialog}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AbstractOkCancelDialog}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Margin="5"
Command="{TemplateBinding OkCommand}"
CommandParameter="{TemplateBinding OkCommandParameter}"
Content="Ok"
DockPanel.Dock="Right" />
<Button
Grid.Column="2"
Margin="5"
Command="{TemplateBinding CancelCommand}"
CommandParameter="{TemplateBinding CancelCommandParameter}"
Content="Cancel"
DockPanel.Dock="Right" />
</Grid>
</Grid>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now you can create your individual dialogs like you would create any other window
Brief example:
TestDialog.xaml
<local:AbstractOkCancelDialog
x:Class="WpfApp.TestDialog"
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:local="clr-namespace:WpfApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="TestDialog"
Width="800"
Height="450"
OkCommand="{x:Static local:Commands.OkWindowCommand}"
OkCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
CancelCommand="{x:Static local:Commands.CancelWindowCommand}"
CancelCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid>
<!-- Content -->
</Grid>
</local:AbstractOkCancelDialog>
TestDialog.xaml.cs
public partial class TestDialog : AbstractOkCancelDialog
{
...
}
i´m a newbie in WPF and have a Problem with data-binding. I hope you can help me:
Here is my XAML- Code:
<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" Height="350" Width="525">
<Grid>
<TabControl Name="MainTabControl" ItemsSource="{Binding}" Margin="2,0,-2,0">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Header}" Tag="{Binding Rid}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Dg}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
So i have a TabControl with a binding to a ObservableCollection of Tabs. Each Tab has a TextBlock as Header and a DataGrid.
Binding:
public MainWindow()
{
InitializeComponent();
DataGrid dg = new DataGrid();
DataGridTemplateColumn xx = new DataGridTemplateColumn()
{
Header = new TextBlock() { Text = "Edit", Tag = "Edit" }
};
xx.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
dg.Columns.Add(xx);
OCTabs.Add(new OCTab() { Header = "test1", Rid = 1 , Dg = dg});
OCTabs.Add(new OCTab() { Header = "test2", Rid = 2 , Dg = dg});
OCTabs.Add(new OCTab() { Header = "test3", Rid = 3, Dg = dg });
MainTabControl.DataContext = OCTabs;
}
and here the Tab- Class:
public class OCTab : INotifyPropertyChanged
{
private string _header;
private int _rid;
private DataGrid dg;
public DataGrid Dg { get { return dg; } set { dg = value; NotifyPropertyChanged("Dg"); } }
public string Header { get { return _header; } set { _header = value; NotifyPropertyChanged("Header"); } }
public int Rid { get { return _rid; } set { _rid = value; NotifyPropertyChanged("Rid"); } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Now my problem:
everything works fine, except for the DataGrids from each Tab. The DataGrid is empty (no Columns,...)
The Binding doesn´t work and i don´t know why...
EDIT:
so is it possible, that each Tabitem has it´s own datagrid?
I have a solution with 2 projects: Windows Phone App and a Windows Phone Class Library. The class library has a control called MessageBoxExtended which inherits from ContentControl. The project also has a Themes folder with a generic.xaml file. The file has the Build Action set to Page and it looks like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:KontactU.Common.WPControls;assembly=KontactU.Common.WPControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Style TargetType="local:MessageBoxExtended">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MessageBoxExtended">
<Grid x:Name="LayoutRoot">
<StackPanel>
<TextBlock x:Name="lblTitle" Text="Title Goes Here" Style="{StaticResource PhoneTextTitle3Style}"/>
<TextBlock x:Name="lblMessage" Text="Some long message here repeated over and over again. Some long message here repeated over and over again. " TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="btnLeft" Content="Button1" Click="btnLeft_Click"></Button>
<Button x:Name="btnCenter" Content="Button2" Click="btnCenter_Click"></Button>
<Button x:Name="btnRight" Content="Button3" Click="btnRight_Click"></Button>
</StackPanel>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The control code looks like this:
public class MessageBoxExtended : ContentControl
{
private TextBlock lblTitle;
private TextBlock lblMessage;
private Button btnLeft;
private Button btnCenter;
private Button btnRight;
private bool currentSystemTrayState;
internal Popup ChildWindowPopup
{
get;
private set;
}
private static PhoneApplicationFrame RootVisual
{
get
{
return Application.Current == null ? null : Application.Current.RootVisual as PhoneApplicationFrame;
}
}
public MessageBoxExtended()
: base()
{
DefaultStyleKey = typeof(MessageBoxExtended);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
lblTitle = this.GetTemplateChild("lblTitle") as TextBlock;
lblMessage = this.GetTemplateChild("lblMessage") as TextBlock;
btnLeft = this.GetTemplateChild("btnLeft") as Button;
btnCenter = this.GetTemplateChild("btnCenter") as Button;
btnRight = this.GetTemplateChild("btnRight") as Button;
InitializeMessageBoxExtended("Title", "Message", MessageBoxExtendedButtonType.Ok);
}
private void InitializeMessageBoxExtended(string title, string message, MessageBoxExtendedButtonType buttonType)
{
HideSystemTray();
lblTitle.Text = title;
lblMessage.Text = message;
switch (buttonType)
{
case MessageBoxExtendedButtonType.Ok:
btnLeft.Visibility = System.Windows.Visibility.Collapsed;
btnRight.Visibility = System.Windows.Visibility.Collapsed;
btnCenter.Content = "ok";
break;
case MessageBoxExtendedButtonType.OkCancel:
btnCenter.Visibility = System.Windows.Visibility.Collapsed;
btnLeft.Content = "ok";
btnRight.Content = "cancel";
break;
case MessageBoxExtendedButtonType.YesNo:
btnCenter.Visibility = System.Windows.Visibility.Collapsed;
btnLeft.Content = "yes";
btnRight.Content = "no";
break;
}
}
public void Show(string title, string message, MessageBoxExtendedButtonType buttonType)
{
if (ChildWindowPopup == null)
{
ChildWindowPopup = new Popup();
try
{
ChildWindowPopup.Child = this;
}
catch (ArgumentException)
{
throw new InvalidOperationException("The control is already shown.");
}
}
if (ChildWindowPopup != null && Application.Current.RootVisual != null)
{
// Configure accordingly to the type
InitializeMessageBoxExtended(title, message, buttonType);
// Show popup
ChildWindowPopup.IsOpen = true;
}
}
private void HideSystemTray()
{
// Capture current state of the system tray
this.currentSystemTrayState = SystemTray.IsVisible;
// Hide it
SystemTray.IsVisible = false;
}
}
The Windows Phone App references it and calls it in the code behind by instantiating it and calling the Show method:
MessageBoxExtended mbe = new MessageBoxExtended();
mbe.Show();
The problem is that the OnApplyTemplate is never called. I've tried commenting out all the lines in the generic.xaml, but I get the same result.
Any ideas?
Never mind, it was my mistake. I added
if (lblTitle == null)
return;
to the InitializeMessageBoxExtended() method and now it works. If you follow the logic the constructor is called before the OnApplyTemplate() which calls the InitializeMessageBoxExtended() and therefore the values are null. By adding the code above the control doesn't throw an exception, it continues and when the control is part of the VisualTree the OnApplyTemplate is called.
Hope this helps anyone out there.
I am trying to bind recursively to the children of an item in a TreeView. From what I can see on MSDN HierarchicalDataTemplate is the way to go, but thus far I've only been partially successful.
My class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DocumentText test = new DocumentText();
this.DataContext = test;
for (int i = 1; i < 5; i++)
{
test.AddChild();
}
foreach (DocumentText t in test.Children)
{
t.AddChild();
t.AddChild();
}
}
}
partial class DocumentText
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
public override string ToString()
{
return Name;
}
public List<DocumentText> _children;
public List<DocumentText> Children
{
get { return this._children; }
}
public DocumentText()
{
_name = "Test";
_children = new List<DocumentText>();
}
public void AddChild()
{
_children.Add(new DocumentText());
}
}
My XAML:
In mainview.xaml:
<Window x:Class="treetest.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">
<Grid>
<TreeView Name="binderPanel" DockPanel.Dock="Left"
MinWidth="150" MaxWidth="250" Background="LightGray"
ItemsSource="{Binding Children}">
</TreeView>
</Grid>
</Window>
In app.xaml:
<HierarchicalDataTemplate x:Key="BinderTemplate"
DataType="{x:Type src:DocumentText}" ItemsSource="{Binding Path=/Children}">
<TreeViewItem Header="{Binding}"/>
</HierarchicalDataTemplate>
This code produces a list of the first children, but the nested children are not displayed.
The primary problem in what you posted is that you haven't connected the HierarchicalDataTemplate as the TreeView's ItemTemplate. You need to either set ItemTemplate="{StaticResource BinderTemplate}" or remove the x:Key to apply the template to all DocumentText instances. You should also change the TreeViewItem in the template to a TextBlock - the TreeViewItem is generated for you and what you put in that template is applied to it as a HeaderTemplate.
I have list of checkboxes on a window specifying some items to be ordered. I need to first disable the Order button when the windows loads and enable it after selecting/check some items(checkboxes) and vice versa. I have bind the IsChecked property of the checkbox.
Edit Import from OP comment:-
I have only one checkbox in the ItemsControl. and I have bind the ItemsControl's ItemsSource to List. that way we can show multiple checkboxes as per the items in the List.
Here is the code:
<ItemsControl ItemsSource="{Binding FavoriteItems}" Margin="80,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<Grid>
<CheckBox IsChecked="{Binding IsHouseholdSelected}" Content="{Binding SubCategoryName}" Grid.ColumnSpan="1" FontFamily="Calibri" FontWeight="Bold" />
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Below is a sample code that could help you out. Basically, the key here is I had the Items in the list implicitly notify its parent ViewModel's Command object to raise the CanExecuteChanged event every time the IsChecked property changes. (Also, I'm using "DelegateCommand" here, which is just the same as "RelayCommand").
ViewModels:
public class ViewModel : INotifyPropertyChanged
{
public DelegateCommand MyCommand { get; set; }
private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<Item> Items
{
get { return this.items; }
}
public ViewModel()
{
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 1" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 2" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 3" });
this.MyCommand = new DelegateCommand(this.CanExecute, this.Execute);
}
public void Execute(object parameter)
{
MessageBox.Show("Executed");
}
public bool CanExecute(object parameter)
{
return (this.items.Count == this.items.Count((x) => x.IsChecked));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ItemViewModel
{
private ViewModel parent;
private bool isChecked;
public string Text { get; set; }
public bool IsChecked
{
get { return this.isChecked; }
set
{
this.isChecked = value;
if (this.parent.MyCommand != null)
this.parent.MyCommand.OnCanExecuteChanged(null);
}
}
public Item(ViewModel parent)
{
this.parent = parent;
}
}
View:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<DockPanel>
<Button DockPanel.Dock="Bottom" Command="{Binding MyCommand}">Test</Button>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</Window>
Bind a command to the button and implement the CanExecute method to check the status of the checkboxes and either enable or disable the button and use the Execute method to invoke the functionality that you want on the button.
MVVM RelayCommand
CanExecute on MSDN
EDIT: Here is some source code of how to implement a RelayCommand. The RelayCommand class can be found at the first link provided above. I'm assuming that you know how to hook up the DataContext to the ViewModel implementation.
<StackPanel>
<CheckBox Name="MyCheckBox" Content="Some CheckBox"
IsChecked="{Binding MyCheckBoxChecked}"/>
<Button Content="Click me" Command="{Binding MyCommand}"/>
</StackPanel>
public class OrderViewModel
{
private RelayCommand MyRelayCommand;
public OrderViewModel()
{
MyRelayCommand = new RelayCommand(Execute, CanExecute);
MyCheckBoxChecked = false;
}
public RelayCommand MyCommand
{
get { return MyRelayCommand; }
}
public bool MyCheckBoxChecked { get; set; }
private bool CanExecute(object o)
{
// Here I'm just checking the property we've bound to but you can put
// anything in here that will return a bool, including a check of any/all
// of the checkboxes you may need to check
return MyCheckBoxChecked;
}
private void Execute(object o)
{
Console.WriteLine(#"Executing ...");
}
}