This question is a continuation of the pregoing one.( How can I combine some UserControls in SilverLight?)
I have 3 view models with different colour properties.
How can I create elements of User Control with trigger invoke method after pressing the button on the element.
Here is a code of this element that I have upgrade with the trigger action.
<UserControl x:Class="SilverlightApplication14.NodePicture"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SilverlightApplication14"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
HorizontalAlignment="Center">
<Canvas x:Name="ParentCanvas" Background="{Binding NodeColor}" Width="100" Height="100" >
</Canvas>
<Image HorizontalAlignment="Center"
Source="add.png"
Stretch="Fill"
Width="16"
VerticalAlignment="Top"
Margin="0,0,2,2"
Height="16" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<local:Add />
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
</Grid>
</UserControl>
And the code with the trigger action
namespace SilverlightApplication14
{
public class Add : TriggerAction<FrameworkElement>
{
protected override void Invoke(object parameter)
{
var vm = AssociatedObject.DataContext as NodeViewModel;
if (vm != null)
{
if (vm.Nodes == null)
{
vm.Nodes = new ObservableCollection<NodeViewModel>();
}
var child = new NodeViewModel { NodeColor = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0)) };
vm.Nodes.Add(child);
}
}
}
}
Updated code:
<Grid>
<Grid.Resources>
<Style x:Key="myStyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="Khaki" />
<Setter Property="Foreground" Value="DarkSlateGray" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontStyle" Value="Italic" />
<Setter Property="FontSize" Value="14" />
<Setter Property="BorderBrush" Value="DarkGray" />
</Style>
</Grid.Resources>
<ListBox ItemsSource="{Binding Nodes}" ItemContainerStyle="{StaticResource myStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:NodePicture DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
Is there a simple (or a right way ) way of doing this?
It is preferable to work with business-logic in view models, whereas triggers are intended for working with UI.
I would change the trigger to a command:
<Button Command="{Binding AddCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Image ... />
</ControlTemplate>
</Button.Template>
</Button>
When a user clicks the button, the AddCommand is invoked. It can be implemented in the view model so:
public class NodeViewModel
{
public NodeViewModel()
{
this.AddCommand = new RelayCommand(obj => { /* do something */ });
}
public RelayCommand AddCommand { get; private set; }
//...
}
The RelayCommand class is one of the possible implementations and it can be downloaded with the MVVM Light framework here.
Related
I'm trying to create a menu that works with radio buttons. The buttons are graphically prettied by a template. here I would like to display an icon and a text. However, I don't know how I can pass several parameters, so far I only pass the text and have not yet found a way to pass the image.
<StackPanel Grid.Row="1" Margin="0,10,0,0">
<RadioButton Content="Dashboard"
IsChecked="True"
Style="{StaticResource MenuButtonTheme}"/>
<RadioButton Content="Product"
Style="{StaticResource MenuButtonTheme}"/>
<RadioButton Content="Inventory"
Style="{StaticResource MenuButtonTheme}"/>
</StackPanel>
Style Of the Radiobutton
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}"
x:Key="MenuButtonTheme">
<Style.Setters>
<Setter Property="Foreground" Value="#FFFFFF"/>
<Setter Property="FontFamily" Value="/Fonts/#Poppins"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border Background="{TemplateBinding Background}"
CornerRadius="5"
Margin="5,0,5,0">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Height="50"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<iconPacks:PackIconBoxIcons Kind="SolidPieChartAlt2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10,0,0,0"/>
<TextBlock Grid.Column="1" Text="{TemplateBinding Property=Content}"
VerticalAlignment="Center"
FontSize="20"
FontWeight="Regular"
Margin="10,0,0,0"/>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Background" Value="#212121"/>
<Setter Property="Foreground" Value="#4169E1"/>
</Trigger>
</Style.Triggers>
</Style>
Foreach particular part of UI in your application, I recommend you to make it a module, that is, a UserContol or ContentControl(recommened). These controls corresponds to View in MVVM, and foreach of them you should add a View Model.
namespace MyNameSpace{
public class View<T> : ContentControl {
public T ViewModel {
get { return (T)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(T), typeof(View<T>), new PropertyMetadata());
}
public abstract class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If the relative logic is purely UI, then Model is not needed in this case.
Your View's xaml should look like this:
<local:View x:TypeArguments="MyAViewModel" x:Name="view"
x:Class="MyNameSpace.MyAView"
skip
xmlns:local="clr-namespace:MyNameSpace">
<Image Source="{Binding ViewModel.ImageSource,ElementName=view}"/>
</local:View>
Your ViewModel should look like this:
public class MyAViewModel: ViewModel {
public AbilityViewModel() {//Constructor with parameter
//Set the image source here
}
private ImageSource imageSource;
public ImageSource ImageSource{
get => imageSource
set{
imageSource = value;
NotifyPropertyChanged();
}
}
}
In the root element of your UI hierarchy, for example your MainWindow, add your custom contols:
<Window x:Name="window" skip>
<Grid>
<local:MyAView ViewModel="{Binding MyAViewModel,ElementName=window}"/>
<local:MyBView ViewModel="{Binding MyBViewModel,ElementName=window}"/>
</Grid>
</Window>
You may either do so with adding dependency properies of the MyAViewModel and MyBViewModel to your MainWindow, or just set MyAView's ViewModel in MainWindow's constructor or loaded event. You may create the ViewModel to pass to view, in which ImageSource is initialized in constructor, or change it after its construction by somewhere in your code.
Above codes are just demo, directly written in stackoverflow's webpage and is not tested. You may ask me if there is any problem.
I am using caliburn micro on my project. So far I have had no problems with ActivateItemAsync. Now, however, this method does not activate my ActiveItem. Now what my code looks like:
I have a DashboardMainView:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Ribbon ...>
</Ribbon>
<Grid Grid.Row="1" Background="#E8E8E8">
<ContentControl x:Name="ActiveItem" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
</ContentControl>
</Grid>
Then in DashboardMainViewModel:
public class DashboardMainViewModel : Conductor<object>
{
private IDialogCoordinator dialogCoordinator;
public DashboardMainViewModel(IDialogCoordinator instance)
{
this.dialogCoordinator = instance;
ActivateItemAsync(new DashboardSummaryViewModel());
}
public async Task Execution()
{
await ActivateItemAsync(new BasicExecutionViewModel(DialogCoordinator.Instance));
}
So far everything works, but then BasicExecutionView is activated, where I have a DataGrid and MenuContext in it. And here comes the problem, when we right-click on the DataGrid, a menu pops up where, after selecting an interesting option, it should activate another view ... but it does not. My code looks like this:
<Grid >
<Viewbox Stretch="Fill">
<DataGrid
x:Name="BasicExecutionGrid"
CanUserSortColumns="True"
IsReadOnly="True"
CanUserAddRows="False"
FontSize="11"
Height ="800"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" AutoGeneratingColumn="BasicExecutionGrid_AutoGeneratingColumn">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap"
Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Background" Value="LightSteelBlue"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Height" Value="45"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Details"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.ActivateCellDetailsView}"
>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Viewbox>
</Grid>
In BasicExecutionViewModel I have:
class BasicExecutionViewModel : Conductor<object>
{
private IDialogCoordinator dialogCoordinator;
private RelayCommand activateCellDetailsView { get; set; }
public BasicExecutionViewModel(IDialogCoordinator instance)
{
this.dialogCoordinator = instance;
}
public ICommand ActivateCellDetailsView
{
get
{
if (activateCellDetailsView == null)
{
activateCellDetailsView = new RelayCommand(async p=> await ActivateCellView());
}
return activateCellDetailsView;
}
}
public async Task ActivateCellView()
{
await ActivateItemAsync(new CellDetailsExecutionViewModel());
}
}
The code comes to ActivateCellView () and activates it. I can see it in Output Window where ActiveItem takes the value of the CellDetailsExecutionViewModel () object, but BasicExecutionView is still displayed on the screen. What am I doing wrong? I guess it's something with either DataContext or parent-child issue ... please help :)
PS. I'm not a professional programmer I'm a hobbyist.
Solved
I solved the problem. My mistake was using Conductor<object> incorrectly. In DashboardMainViewModel. When I corrected on
DashboardMainViewModel: Conductor<object>.Collection.OneActive
ant the same in the BasicExecutionViewModel
BasicExecutionViewModel: Conductor<object>.Collection.OneActive
I also updated the code in the ActivateCellView() method to
public async Task ActivateCellView()
{
CellDetailsExecutionViewModel cellDetailsExecutionViewAcvtivate = new CellDetailsExecutionViewModel();
var parentConductor = (Conductor<object>.Collection.OneActive)(this.Parent);
await parentConductor.ActivateItemAsync(cellDetailsExecutionViewAcvtivate);
}
And everything works beautifully
hi in my form(adminentitylist.xaml) file contains the code is below
<ResourceDictionary xmlns:local="clr-namespace:iWatch.Administration"
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"
mc:Ignorable="d"
xmlns:my="clr-namespace:iWatch.UILibrary;assembly=UILibrary"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
xmlns:cmd="clr-namespace:iWatch.UILibrary;assembly=UILibrary"
x:Class="AdminEntityList">
<ControlTemplate x:Key="AddImgBtnTemplate" TargetType="Button">
<Grid>
<Image Name="Normal" Source="/UILibrary;component/Themes/Default/Images/Add.png" Opacity="1.0" Height="18" Width="18"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Opacity" Value="0.5"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Normal" Property="Opacity" Value="0.2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="DelImgBtnTemplate" TargetType="Button">
<Grid>
<Image Name="Normal" Source="/UILibrary;component/Themes/Default/Images/Delete.png" Opacity="1.0" Height="18" Width="18"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Opacity" Value="0.5"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Normal" Property="Opacity" Value="0.2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type local:AdminEntityList}">
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="dx:ThemeManager.ThemeName" Value="Office2007Black"/>
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AdminEntityList}">
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<StackPanel Name="StpFilter" Orientation="Horizontal" HorizontalAlignment="Left" Margin="5">
<ContentControl x:Name="FilterCriteriaArea" ContentTemplate="{TemplateBinding FilterCriteriaArea}"/>
</StackPanel>
<StackPanel Name="StpCountry" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5" >
<Label Content="Select Country" Name="lblCountry" Style="{StaticResource lblNormal}" Margin="10,0,5,7" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Visibility="Collapsed"/>
<ComboBox Name="cbCountry" Width="170" Style="{StaticResource ddlNormal}" SelectedValue="{Binding CountryKey,Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5,2,10,0" Visibility="Collapsed"/>
<Button Content="Filter" Name="btnFilter" Width="70" Height="23" Margin="0,0,10,0" Visibility="Collapsed"/>
</StackPanel>
<StackPanel Name="actinact" Orientation="Horizontal" HorizontalAlignment="Left" Margin="5">
<CheckBox Name="chkactive" Height="18" Content="Active" Checked="chkactive_Checked"></CheckBox>
<CheckBox Name="chkinactive" Height="18" Content="In-Active"/>
</StackPanel>
<StackPanel Name="StpAddDel" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5">
<Button Name="BtnAdd" Height="20" Width="20" Margin="5,0" Template="{StaticResource AddImgBtnTemplate}" />
<Button Name="BtnDel" Height="20" Width="20" Margin="5,0" Template="{StaticResource DelImgBtnTemplate}" />
</StackPanel>
</DockPanel>
<DockPanel Name="AMLDP">
<dxg:GridControl Name="genericGrid" AutoPopulateColumns="true" ShowBorder="False"
DesignTimeDataSourceRowCount="50" DataSource="{Binding}"
Padding="0" Margin="0,0,0,0" IsManipulationEnabled="True" dx:ThemeManager.ThemeName="CPCEGridTheme">
<dxg:GridControl.Resources>
<ResourceDictionary x:Key="CPCEGridTheme" Source="/UILibrary;component/Themes/Default/Styles/DataGrid/CPCEGridTheme.xaml" />
</dxg:GridControl.Resources>
<dxg:GridControl.View>
<dxg:TableView x:Name="tblGeneric" MultiSelectMode="Row" AllowGrouping="True" ClipToBounds="True" AutoWidth="True"
ShowGroupPanel="False" ShowHorizontalLines="True" ShowVerticalLines="False" AllowMoveColumnToDropArea="False" AllowDrop="False"
IsGroupPanelMenuEnabled="False" AllowEditing="False" NavigationStyle="Cell" Margin="0"
GroupRowStyle="{StaticResource OddEvenRowStyle}" VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard" AllowHorizontalScrollingVirtualization="True"
RowStyle="{StaticResource OddEvenRowStyle}" CellStyle="{StaticResource FocusedCellStyle}">
<dxg:TableView.RowCellMenuCustomizations>
<dxb:BarButtonItem Name="ctmView" IsVisible="False" Content="View" Command="{x:Static cmd:CustomCommands.View}" />
<dxb:BarButtonItem Name="ctmEdit" IsVisible="True" Content="Edit" Command="{x:Static cmd:CustomCommands.Edit}" />
<dxb:BarButtonItem Name="ctmMIQ" IsVisible="False" Content="Manage Interview Questions" Command="{x:Static cmd:CustomCommands.ManageInterviewQuestions}" />
<dxb:BarButtonItem Name="ctmActivate" IsVisible="False" Content="Activate" Command="{x:Static cmd:CustomCommands.Activate}" />
<dxb:BarButtonItem Name="ctmCopyInterviewType" IsVisible="False" Content="Copy Interview Type" Command="{x:Static cmd:CustomCommands.CopyInterviewType}" />
<dxb:BarButtonItem Name="ctmAssignInvGroup" IsVisible="False" Content="Assign Investigative Group" Command="{x:Static cmd:CustomCommands.AssignInvestigativeGroup}" />
<dxb:BarButtonItem Name="ctmCopySelected" IsVisible="False" Content="Copy From Selected" Command="{x:Static cmd:CustomCommands.CopyInterviewTemplate}" />
</dxg:TableView.RowCellMenuCustomizations>
</dxg:TableView>
</dxg:GridControl.View>
<dxg:GridControl.CommandBindings>
<CommandBinding x:Name="cmdEdit" Command="{x:Static cmd:CustomCommands.Edit}" />
<CommandBinding x:Name="cmdView" Command="{x:Static cmd:CustomCommands.View}" />
<CommandBinding x:Name="cmdMIQ" Command="{x:Static cmd:CustomCommands.ManageInterviewQuestions}" />
<CommandBinding x:Name="cmdActivate" Command="{x:Static cmd:CustomCommands.Activate}" />
<CommandBinding x:Name="cmdCopyInterviewType" Command="{x:Static cmd:CustomCommands.CopyInterviewType}" />
<CommandBinding x:Name="cmdAssignInvestigativeGroup" Command="{x:Static cmd:CustomCommands.AssignInvestigativeGroup}" />
<CommandBinding x:Name="cmdCopySelected" Command="{x:Static cmd:CustomCommands.CopyInterviewTemplate}" />
</dxg:GridControl.CommandBindings>
</dxg:GridControl>
</DockPanel>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
in the above code i created the checkbox checked event Checked="chkactive_Checked"
private void chkactive_Checked(object sender, RoutedEventArgs e)
{
}
but the event is not firing and i am getting an error is
Error 14 'AdminEntityList' does not contain a definition for
'chkactive_Checked' and no extension method 'chkactive_Checked'
accepting a first argument of type 'AdminEntityList' could be found
(are you missing a using directive or an assembly reference?)
please help me with is problem...
Here is a simple example:
<Window x:Class="CheckboxCheckedCommand.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>
<CheckBox Content="Case Sensitive" Command="{Binding checkedCommand}"/>
</Grid>
Codebehind:
public partial class MainWindow : Window
{
public ViewModel vm { get; set; }
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
this.DataContext = vm;
}
}
ViewModel:
public class ViewModel
{
public ICommand checkedCommand { get; set; }
public ViewModel()
{
checkedCommand = new CheckedCommand(this);
}
public void CheckedHandler()
{
//todo - implement your handler
}
}
And of course the Checked Unchecked command:
public class CheckedCommand : ICommand
{
private ViewModel _vm = null;
public CheckedCommand(ViewModel _viewModel)
{
_vm = _viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_vm.CheckedHandler();
}
}
Let me know if you need more details, maybe a parameter which has to be sent from the view and received by the handler..
I see this as an easy and normal flow for MVVM. You have some instance/static method issue right there.. try the above approach, in my opinion is cleared and helps you decouple the things.
Oh, i forgot to say, Command="{Binding checkedCommand}" , is both applicable for Checked and Unchecked events.
Update 1
This can be your codebehind:
public partial class MainWindow : Window
{
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(MainWindow), new PropertyMetadata(false, new PropertyChangedCallback(PropertyChanged)));
private static void PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
//textbox.ScrollToEnd(); //An object reference is required for the non-static field.
MainWindow localWindow = (MainWindow)obj;
Console.WriteLine(localWindow.TestString);
}
public string TestString { get; set; }
public MainWindow()
{
InitializeComponent();
TestString = "test";
this.DataContext = this;
}
}
And you CheckBox definition is this now:
<CheckBox Content="Case Sensitive" IsChecked="{Binding IsChecked}"/>
You basically define that DependencyProperty with the name IsChecked. Each time you check or uncheck it, the callback method defined there will be called. And may see an example on how to access an instance field from that static method. Good luck!
Imagine multiple object types in one Listbox. Both have different look and presented information. If I add any of them in ObservableCollection it displays them fine in ListBox.
<DataTemplate DataType="{x:Type local:DogData}" >
<Grid...
</DataTemplate>
<DataTemplate DataType="{x:Type local:CatData}" >
<Grid...
</DataTemplate>
Now I want users to be able to press a button and switch view to see more detailed information and there templates which provide it
<DataTemplate x:Key="TemplateDogDataWithImages" >
<Grid...
</DataTemplate>
<DataTemplate x:Key="TemplateCatDataWithImages" >
<Grid...
</DataTemplate>
but I can assign only one
AnimalsListBox.ItemTemplate = this.Resources["TemplateDogDataWithImages"] as DataTemplate;
I don't want to have one and have a bunch of triggers in it.
I have researched DataTemplateSelectors
http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector
Is there a better way? Is there a way to switch default templates for each data type so I could avoid DataTemplateSelectors?
On click of the button how do I select TemplateDogDataWithImages and TemplateCatDataWithImages as default ones and cliking other button use TemplateDogDataSimple and TemplateCatDataSimple?
I think a selector is easier (less code), but if you really want to use triggers, maybe something like this?
namespace WpfApplication65
{
public abstract class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public abstract class DataBase : NotifyBase
{
bool m_showDetailed;
public bool ShowDetailed
{
get { return m_showDetailed; }
set
{
m_showDetailed = value;
NotifyPropertyChanged("ShowDetailed");
}
}
}
public class DogData : DataBase { }
public class CatData : DataBase { }
public partial class MainWindow : Window
{
public List<DataBase> Items { get; private set; }
public MainWindow()
{
Items = new List<DataBase>() { new DogData(), new CatData(), new DogData() };
DataContext = this;
InitializeComponent();
}
}
}
<Window x:Class="WpfApplication65.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication65"
Title="MainWindow"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate DataType="{x:Type l:CatData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic cat view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed cat view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Red"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate DataType="{x:Type l:DogData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic dog view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed dog view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Blue"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Items}" />
</Window>
This is probably a newbie question but I’m breaking my head about this one.
I’m building an application in WPF using the MVVM pattern. I have a view which has fields binded a property exposing the current customer entity. And within the view I have a command to change the bank account numbers belonging to that customer. The command called take the whole sub-entity as parameter. Then call another function which also takes the sub-entity as parameter and passes it to a new viewmodel binded to a new view which is displayd as a dialog for change. This all works. But when I’m change the bank account number in the dialog the original view also changes the account number number real-time. They are still connected to each other. I want to cancel this link to be able to cancel the dialog and the changes I made within that dialog. But I can’t get this to work.
Code say’s more the words.
View MAIN
<dxlc:LayoutGroup Header="Rekeningen" View="GroupBox" Orientation="Vertical" VerticalAlignment="Stretch">
<dxlc:LayoutItem>
<StackPanel>
<Button Content="{x:Static language:Common.NieuwRekeningnrToevoegen}" Command="{Binding NieuwRekeningCommand}" />
<ListView ItemsSource="{Binding CurrentRelatie.Rekeningnummers}" ItemTemplate="{StaticResource RelatieRekeningnrTemplate}" />
</StackPanel>
</dxlc:LayoutItem>
</dxlc:LayoutGroup>
View item template MAIN
<DataTemplate x:Key="RelatieRekeningnrTemplate">
<Grid>
<TextBlock >
<Run Text="{Binding Omschrijving}" FontWeight="Bold" FontStyle="Italic" /> <LineBreak/>
<Run Text="{Binding RekNummer}" /> - <Run Text="{Binding BicNummer}" FontStyle="Italic" />
</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button Command="{Binding DataContext.VerwijderRekeningCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template" Value="{DynamicResource tplFlatButton}" />
</Style>
</Button.Style>
<Path Height="9" Stretch="Uniform" Fill="{DynamicResource AccentColorDarkGray}" Data="{DynamicResource Delete}" />
</Button>
<Button Command="{Binding DataContext.EditRekeningCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template" Value="{DynamicResource tplFlatButton}" />
</Style>
</Button.Style>
<Path Height="10" Stretch="Uniform" Fill="{DynamicResource AccentColorDarkGray}" Data="{DynamicResource Edit}" >
</Path>
</Button>
</StackPanel>
</Grid>
</DataTemplate>
Viewmodel
private model.Relatie _CurrentRelatie = null;
public model.Relatie CurrentRelatie
{
get { return _CurrentRelatie; }
set { SetProperty(ref _CurrentRelatie, value, () => CurrentRelatie); }
}
public ICommand EditRekeningCommand { get; private set; }
void OnEditRekeningExecute(model.Rekeningnummer Rek)
{
EditRekeningnummer(Rek);
}
private void EditRekeningnummer(model.Rekeningnummer Rek)
{
Dialog.dRekeningnummerEditViewModel ReknummerVM = new Dialog.dRekeningnummerEditViewModel();
ReknummerVM.SetRekening(Rek);
UICommand ResCommand = DialogService.ShowDialog(ReknummerVM.DialogUICommand, string.Format("{0} {1}", Common.Rekening, Rek.Omschrijving ?? Rek.RekNummer), "viewdRekeningnummerEdit", ReknummerVM);
if (ResCommand == null || ResCommand.IsCancel == true)
return;
}
View RekeningnummerEdit
<dxlc:LayoutGroup Orientation="Vertical">
<dxlc:LayoutItem Label="{Binding CurrentRekening, ConverterParameter=Omschrijving, Converter={StaticResource ModelToDisplay}}">
<dxe:TextEdit EditValue="{Binding CurrentRekening.Omschrijving, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" ValidateOnTextInput="True" utils:FocusAdvancement.AdvancesByEnterKey="true"/>
</dxlc:LayoutItem>
<dxlc:LayoutItem Label="{Binding CurrentRekening, ConverterParameter=RekNummer, Converter={StaticResource ModelToDisplay}}">
<dxe:TextEdit EditValue="{Binding CurrentRekening.RekNummer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" ValidateOnTextInput="True" utils:FocusAdvancement.AdvancesByEnterKey="true"/>
</dxlc:LayoutItem>
<dxlc:LayoutItem Label="{Binding CurrentRekening, ConverterParameter=BicNummer, Converter={StaticResource ModelToDisplay}}">
<dxe:TextEdit EditValue="{Binding CurrentRekening.BicNummer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" ValidateOnTextInput="True" utils:FocusAdvancement.AdvancesByEnterKey="true"/>
</dxlc:LayoutItem>
</dxlc:LayoutGroup>
Viewmodel RekeningnummerEdit
public dRekeningnummerEditViewModel()
{
DialogUICommand = new List<UICommand>();
AnnuleerUICommand = new UICommand() {
Caption=Common.Annuleren,
Id = MessageBoxResult.Cancel,
IsCancel=true
};
OKUICommand = new UICommand() {
Caption=Common.Opslaan,
Id = MessageBoxResult.OK,
IsDefault=true
};
DialogUICommand.Add(OKUICommand);
DialogUICommand.Add(AnnuleerUICommand);
CurrentRekening = new model.Rekeningnummer();
}
public void SetRekening(model.Rekeningnummer Rek)
{
CurrentRekening = Rek;
IsInEditMode = true;
}
#region "Properties"
private model.Rekeningnummer _CurrentRekening;
public model.Rekeningnummer CurrentRekening
{
get { return _CurrentRekening; }
set { SetProperty(ref _CurrentRekening, value, () => CurrentRekening); }
}
#endregion
#region "Private function"
#endregion
#region "Commands"
public List<UICommand> DialogUICommand { get; private set; }
protected UICommand AnnuleerUICommand { get; private set; }
protected UICommand OKUICommand { get; private set; }
The behaviour you're seeing is because you are passing an object reference (model.Rek) from your view to your dialog. Therefore when your dialog changes the values of the model.Rek, the changes are immediately reflected in your view.
A common approach to solve this problem is to:
Clone (copy) your model, i.e. make a new object with the same values. You can use the ICloneable interface as a standard pattern (MemberwiseClone can help here if you only need a shallow copy)
Send the clone to your dialog
If the user presses OK, then take the values of the clone and copy them back to the original model. If the user presses Cancel, then do nothing more