WPF Changing Datacontexts and Views in the Same Window
I am new to WPF am and is porting my application from VC ++ 6.0 / MFC to C # / WPF (VS2013). Most of my windows development has been in VC ++ / MFC. I'm trying to stick with the MVVM pattern and writing some proof-of-concept applications to keep my feet wet. So far, I have one point.
When my application starts up, it will present a tree view of customers and invoices. It works well for me using a simple hierarchical data template with each level binding to my local data type (view model). I want this to happen when the bill is selected (right now I have a button to click on the invoice template). I want the treeview to be replaced with a detailed account view (I don't want the dialog to pop up to the top).
Xaml for this:
<DockPanel>
<TreeView x:Name="trvGroups" ItemsSource="{Binding LBGroups}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<TreeView.ItemContainerStyle>
<!--
This Style binds a TreeViewItem to a LBtreeViewItemViewModel
-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:BillViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BillName}" />
<Button Command="{Binding Path=BillEditCommand}">Edit</Button>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</DockPanel>
Now I have more questions than anything. Should I define each view as a custom control and put it in window.resources? Am I using data templates? I guess I would change the data context to point to the detail invoice view model. What's the best way to do this?
My goal is to stick to MVVM as I understand it - to have nothing in the code (or as little as possible).
I'm looking for more pointers to get me started on the right track as I research. I'm a little woozy at the moment.
Thanks in advance.
source to share
I will show you a simple basic details scenario where you can select models in your TreeView and Edit Them.
CS:
public partial class MainWindow : Window , INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private ICommand onEditBillCommand;
public ICommand OnEditBillCommand
{
get
{
if (onEditBillCommand == null)
onEditBillCommand = new RelayCommand<Bill>
(
bill => { CurrentBill = bill; }
);
return onEditBillCommand;
}
}
private Bill currectBill;
public Bill CurrentBill
{
get { return currectBill; }
set
{
currectBill = value;
PropertyChanged(this, new PropertyChangedEventArgs("CurrentBill"));
}
}
public List<Customer> Customers
{
get
{
List<Customer> customers = new List<Customer>();
for (int i = 0; i < 5; i++)
{
customers.Add(CreateMockCustomer(i));
}
return customers;
}
}
private Customer CreateMockCustomer(int g )
{
Customer c = new Customer();
c.Name = "John (" + g + ")" ;
for (int i = 0; i < 3; i++)
{
c.Bills.Add(CreateMockBill());
}
return c;
}
private Bill CreateMockBill()
{
Bill b = new Bill();
b.Price = 55.5;
b.BoughtOnDate = DateTime.Now.Date;
return b;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Customer : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ObservableCollection<Bill> bills;
public ObservableCollection<Bill> Bills
{
get
{
if (bills == null)
{
bills = new ObservableCollection<Bill>();
}
return bills;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Bill : INotifyPropertyChanged
{
private double price;
public double Price
{
get { return price; }
set
{
price = value;
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
private DateTime boughtOnDate;
public DateTime BoughtOnDate
{
get { return boughtOnDate; }
set
{
boughtOnDate = value;
PropertyChanged(this, new PropertyChangedEventArgs("BoughtOnDate"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public interface IRelayCommand : ICommand
{
void RaiseCanExecuteChanged();
}
public class RelayCommand<T> : IRelayCommand
{
private Predicate<T> _canExecute;
private Action<T> _execute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
private void Execute(T parameter)
{
_execute(parameter);
}
private bool CanExecute(T parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public bool CanExecute(object parameter)
{
return parameter == null ? false : CanExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
temp(this, new EventArgs());
}
}
XAML:
<Window>
<Window.Resources>
<HierarchicalDataTemplate x:Key="customerTemplate" DataType="{x:Type local:Customer}" ItemsSource="{Binding Bills}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Price}" />
<TextBlock Text="{Binding BoughtOnDate}" Grid.Column="1" />
<Button Content="Edit" Grid.Column="2"
Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.OnEditBillCommand}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Name}" FontFamily="Arial" FontSize="16" FontWeight="Bold" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="0.05*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Customers}" ItemTemplate="{StaticResource customerTemplate}">
</TreeView>
<Grid Grid.Column="2" DataContext="{Binding CurrentBill, Mode=OneWay}" Background="AliceBlue">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="{Binding Price, Mode=TwoWay}" Margin="50"/>
<TextBox Text="{Binding BoughtOnDate, Mode=TwoWay}" Grid.Row="1" Margin="50"/>
</Grid>
</Grid>
source to share