I have MainWindow thath holds <ContentControl ... Content="{Binding CurrentViewModel}" in MainViewModel I am switching between two View FirstView,SecondView. FirstView contains usercontrol ContentFirstView that implementing async data sending to ContentSecondViewModel. Data send's with time delay 1000ms. The main question is why when i am clicking on 1,2,1,2,1,2 buttons the speed of updating label Count in ContentSecondView greatly increasing? I think that ContentSecondViewModel do not disposing and every time when i am clicking on button 2, it creates new object of Messenger.Register ...
MainModel.xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="1" Content="{Binding CurrentViewModel}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Button Content="1" Command="{Binding FirstCommand}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="2" Command="{Binding SecondCommand}" HorizontalAlignment="Left" Margin="10,78,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
MainViewModel.cs:
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
///
public RelayCommand FirstCommand
{
get
{
return new RelayCommand(() => SwitchFirst());
}
}
public RelayCommand SecondCommand
{
get
{
return new RelayCommand(()=>SwitchSecond());
}
}
public static readonly FirstViewModel firstViewModel = new FirstViewModel();
public static readonly SecondViewModel secondViewModel = new SecondViewModel();
ViewModelBase currentViewModel;
public ViewModelBase CurrentViewModel
{
get
{
return currentViewModel;
}
set
{
if (currentViewModel == value)
return;
currentViewModel = value;
RaisePropertyChanged("CurrentViewModel");
}
}
public void SwitchFirst()
{
CurrentViewModel = MainViewModel.firstViewModel;
}
public void SwitchSecond()
{
CurrentViewModel = MainViewModel.secondViewModel;
}
public MainViewModel()
{
CurrentViewModel = MainViewModel.secondViewModel;
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real"
////}
}
}
SecondView.xaml:
<Grid>
<views:ContentSecondView></views:ContentSecondView>
</Grid>
FirstView.xaml:
<Grid>
<views:ContentFirstView></views:ContentFirstView>
</Grid>
ContentFirstView.xaml:
<Grid>
<Label>View That sending data</Label>
</Grid>
ContentSecondView.xaml:
<Grid>
<Label>View That sending data</Label>
</Grid>
ContentFirstViewModel.cs:
public class ContentFirstViewModel
{
public RelayCommand SendMessage
{
get
{
return new RelayCommand(() => Send());
}
}
public void Send()
{
Messenger.Default.Send<MessageCommuniactor>(new MessageCommuniactor {State=1 });
}
public void Increase()
{
while(true)
{
System.Threading.Thread.Sleep(1000);
Messenger.Default.Send<MessageCommuniactor>(new MessageCommuniactor { State = 1 });
}
}
public ContentFirstViewModel()
{
Action A = new Action(Increase);
IAsyncResult result = A.BeginInvoke(null, null);
}
}
ContentSecondViewModel.cs:
public class ContentSecondViewModel:ViewModelBase
{
private int count;
public int Count
{
get
{
return this.count;
}
set
{
this.count = value;
this.RaisePropertyChanged("Count");
}
}
public ContentSecondViewModel()
{
Messenger.Default.Register<MessageCommuniactor>(this, (key) =>
{
Count += key.State;
});
}
}
Your DataTemplate ensures that a new view is created every time a ContentFirstViewModel is present. Instead, go with a singleton approach where a singleton view is injected when needed. You can use Prism's regionmanager or simple binding for that.
Related
Im trying to create a function in a wpf program, where I can select an item in a listview, and press a button and it changes the tabitem and allows me to then edit the item from the listview that was selected. Im having issues with getting the tabitem to change for me.
For the navigation of my app, I have a ViewModelBase, which my AppointmentsViewModel inherits from. Inside the AppointmentsViewVM there is a tabcontrol with 4 items, by clicking each one it loads the requested view/viewmodel for that function.
This is not the only way I've tried to get this to work, Im currently on day 4. I could get the TabIndex to change in the TabControl earlier, but the tab would still not change for me. So I abandoned that and tried the below route (still no luck).
ViewModelBase
namespace MBR2.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public ICommand MainMenuViewDogs_Command { get; set; }
public ICommand MainMenuViewAppointments_Command { get; set; }
private object _SelectedViewModel;
public object SelectedViewModel
{
get { return _SelectedViewModel; }
set
{
_SelectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
public ViewModelBase()
{
MainMenuViewDogs_Command = new BaseCommand(OpenDogs);
MainMenuViewAppointments_Command = new BaseCommand(OpenAppointments);
}
private void OpenDogs(object obj)
{
SelectedViewModel = new DogsViewModel();
}
private void OpenAppointments(object obj)
{
SelectedViewModel = new AppointmentsViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool _SelectedIndexView;
public bool SelectedIndexView
{
get { return _SelectedIndexView; }
set
{
_SelectedIndexView = value;
OnPropertyChanged("SelectedIndexView");
}
}
private bool _SelectedIndexAdd;
public bool SelectedIndexAdd
{
get { return _SelectedIndexAdd; }
set
{
_SelectedIndexView = value;
OnPropertyChanged("SelectedIndexAdd");
}
}
private bool _SelectedIndexEdit;
public bool SelectedIndexEdit
{
get { return _SelectedIndexEdit; }
set
{
_SelectedIndexView = value;
OnPropertyChanged("SelectedIndexEdit");
}
}
private bool _SelectedIndexDelete;
public bool SelectedIndexDelete
{
get { return _SelectedIndexDelete; }
set
{
_SelectedIndexView = value;
OnPropertyChanged("SelectedIndexDelete");
}
}
}
}
AppointmentsViewModel
{
public class AppointmentsViewModel : ViewModelBase
{
private AppointmentsAddVM _AppointmentsAddVM;
public AppointmentsAddVM AppointmentsAddVM { get { return _AppointmentsAddVM; } }
private AppointmentsEditVM _AppointmentsEditVM;
public AppointmentsEditVM AppointmentsEditVM { get { return _AppointmentsEditVM; } }
private AppointmentsDeleteVM _AppointmentsDeleteVM;
public AppointmentsDeleteVM AppointmentsDeleteVM { get { return _AppointmentsDeleteVM; } }
private AppointmentsViewVM _AppointmentsViewVM;
public AppointmentsViewVM AppointmentsViewVM { get { return _AppointmentsViewVM; } }
public ObservableCollection<object> ViewModelList { get; set; }
public AppointmentsViewModel()
{
this.ViewModelList = new ObservableCollection<object>();
_AppointmentsAddVM = new AppointmentsAddVM();
_AppointmentsEditVM = new AppointmentsEditVM();
_AppointmentsDeleteVM = new AppointmentsDeleteVM();
_AppointmentsViewVM = new AppointmentsViewVM();
this.ViewModelList.Add(_AppointmentsAddVM);
this.ViewModelList.Add(_AppointmentsEditVM);
this.ViewModelList.Add(_AppointmentsDeleteVM);
this.ViewModelList.Add(_AppointmentsViewVM);
}
}
}
AppointmentsView.xaml
<UserControl
x:Class="MBR2.Views.AppointmentsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:vms="clr-namespace:MBR2.ViewModels.Appointments"
xmlns:views="clr-namespace:MBR2.Views.Appointments"
xmlns:viewmodels="clr-namespace:MBR2.ViewModels"
d:DataContext="{d:DesignInstance Type=viewmodels:AppointmentsViewModel}"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type vms:AppointmentsViewVM}">
<views:AppointmentsViewView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:AppointmentsAddVM}">
<views:AppointmentsAddView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:AppointmentsDeleteVM}">
<views:AppointmentsDeleteView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:AppointmentsEditVM}">
<views:AppointmentsEditView />
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="Appointments" Width="Auto" Height="Auto">
<DockPanel HorizontalAlignment="Center"
Height="Auto"
LastChildFill="False"
VerticalAlignment="Top"
Width="Auto">
<TabControl x:Name="VMTabControl">
<TabItem x:Name="ViewTab"
TabIndex="0"
Header="View"
IsSelected="{Binding SelectedIndexView}"
Content="{Binding AppointmentsViewVM}"></TabItem>
<TabItem x:Name="AddTab"
TabIndex="1"
Header="Add"
IsSelected="{Binding SelectedIndexAdd}"
Content="{Binding AppointmentsAddVM}"></TabItem>
<TabItem x:Name="EditTab"
TabIndex="2"
Header="Edit"
IsSelected="{Binding SelectedIndexEdit}"
Content="{Binding AppointmentsEditVM}"></TabItem>
<TabItem x:Name="DeleteTab"
TabIndex="3"
Header="Delete"
IsSelected="{Binding SelectedIndexDelete}"
Content="{Binding AppointmentsDeleteVM}"></TabItem>
</TabControl>
</DockPanel>
</Grid>
</UserControl>
And the associated AppointmentsViewVM
namespace MBR2.ViewModels.Appointments
{
public class AppointmentsViewVM : ViewModelBase, INotifyPropertyChanged
{
private List<AppointmentsView_Wrapper> _AppointmentsView;
public List<AppointmentsView_Wrapper> AppointmentsView
{
get { return _AppointmentsView; }
set
{
_AppointmentsView = value;
OnPropertyChanged("AppointmentsView");
}
}
private List<string> _NameColumn = new List<string>();
public List<string> NameColumn
{
get { return _NameColumn; }
set
{
_NameColumn = value;
OnPropertyChanged("NameColumn");
}
}
private List<string> _ApptDateColumn = new List<string>();
public List<string> ApptDateColumn
{
get { return _ApptDateColumn; }
set
{
_ApptDateColumn = value;
OnPropertyChanged("ApptDateColumn");
}
}
private List<string> _ApptTimeColumn = new List<string>();
public List<string> ApptTimeColumn
{
get { return _ApptTimeColumn; }
set
{
_ApptTimeColumn = value;
OnPropertyChanged("ApptTimeColumn");
}
}
private List<string> _ApptVetColumn = new List<string>();
public List<string> ApptVetColumn
{
get { return _ApptVetColumn; }
set
{
_ApptVetColumn = value;
OnPropertyChanged("ApptVetColumn");
}
}
private List<string> _ApptCreatedColumn = new List<string>();
public List<string> ApptCreatedColumn
{
get { return _ApptCreatedColumn; }
set
{
_ApptCreatedColumn = value;
OnPropertyChanged("ApptCreatedColumn");
}
}
private List<int> _ApptIDColumn = new List<int>();
public List<int> ApptIDColumn
{
get { return _ApptIDColumn; }
set
{
_ApptIDColumn = value;
OnPropertyChanged("ApptIDColumn");
}
}
private string _AppointmentEdit_Enabled = "False";
public string AppointmentEdit_Enabled
{
get { return _AppointmentEdit_Enabled; }
set
{
_AppointmentEdit_Enabled = value;
OnPropertyChanged("AppointmentEdit_Enabled");
}
}
private AppointmentsView_Wrapper _ApptIDSelected;
public AppointmentsView_Wrapper ApptIDSelected
{
get { return _ApptIDSelected; }
set
{
AppointmentEdit_Enabled = "True";
_ApptIDSelected = value;
OnPropertyChanged("ApptIDSelected");
}
}
public AppointmentData AppointmentData = new AppointmentData();
public Messaging Messaging = new Messaging();
public ICommand AppointmentsListView_Command => new DelegateCommand<object>(AppointmentsListView_Clicked);
public ICommand EditSelection_Command => new DelegateCommand<object>(EditSelection_Clicked);
public AppointmentsViewVM()
{
BuildPage();
}
public async void BuildPage()
{
AppointmentsView = await AppointmentData.Appointments_GetAll();
foreach(var item in AppointmentsView)
{
ApptIDColumn.Add(item.ApptID);
NameColumn.Add(item.DogName);
ApptDateColumn.Add(item.ApptDate);
ApptTimeColumn.Add(item.ApptTime);
ApptVetColumn.Add(item.ApptVet);
ApptCreatedColumn.Add(item.ApptCreated.ToString("dd/mm/yyyy"));
}
}
public void AppointmentsListView_Clicked(object obj)
{
Messaging.ShowAlert(ApptIDSelected.ApptID.ToString());
}
public void EditSelection_Clicked(object obj)
{
bool result = Messaging.AskQuestion(ApptIDSelected.ApptID.ToString());
if(result)
{
SelectedIndexView = false;
SelectedIndexAdd = false;
SelectedIndexEdit = true;
SelectedIndexDelete = false;
OnPropertyChanged("SelectedIndexView");
OnPropertyChanged("SelectedIndexAdd");
OnPropertyChanged("SelectedIndexEdit");
OnPropertyChanged("SelectedIndexDelete");
}
else
{
Messaging.ShowAlert("no");
}
}
}
}
Here's a minimal reproduction of something where you select in a listbox and that then selects a corresponding tab in a tabcontrol.
This is very minimal but we can perhaps imagine a more sophisticated viewmodel per item in the listbox with name and viewmodel or something.
This is mainwindow.
<Window.Resources>
<DataTemplate DataType="{x:Type local:Avm}">
<local:Aview/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Bvm}">
<local:Bview/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Cvm}">
<local:Cview/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewmodel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding ViewModels}"
x:Name="lb"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ViewModelName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TabControl Grid.Column="1"
ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding ElementName=lb, Path=SelectedItem, Mode=TwoWay}"
>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding ViewModelName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
I have only done 3 views and viewmodels.
Note that the selecteditem of the listbox is bound twoway to the tabcontrol.
I have matching views and viewmodels A, B and C
MainWindowViewModel
public class MainWindowViewmodel : ViewModelBase
{
public ObservableCollection<Object> ViewModels { get; set; } = new ObservableCollection<Object>{
new Avm{ViewModelName="A viewmodel" },
new Bvm{ViewModelName="B viewmodel" },
new Cvm{ViewModelName="C viewmodel" }
};
}
Both the itemssource of listbox and tabcontrol are bound to that collection of viewmodels. Which are, as I mentioned, as simple as you get really.
Viewmodelbase
public class ViewModelBase : INotifyPropertyChanged
{
public string ViewModelName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Avm, Bvm and Cvm just inherit from that.
An example usercontrol view.
<Grid>
<TextBlock Text="{Binding ViewModelName}"/>
</Grid>
</UserControl>
When I spin that up, select and select an item in the listbox the matching tab is selected. And vice versa. Select a tab and it selects the same one in the listbox.
I'm creating a WPF Molds app that contains 2 windows: MainWindow with DataGrid, and AddEditWindow which allows to Add/Edit Molds.
I have a EditButton which located in a TemplateColumn of DataGrid:
<DataGridTemplateColumn Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="150"
Height="40"
BorderThickness="2"
BorderBrush="DarkRed"
Background="Red"
Foreground="White"
Content="Edit"
Name="BtnEdit"
CommandParameter="{Binding}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.AddEditWindowCommand}">
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
AddEditWindowCommand:
public ICommand AddEditWindowCommand { get; }
private bool CanAddEditWindowCommandExecute(object SelectedRow) => true;
private void OnAddEditWindowCommandExecuted(object SelectedRow)
{
AddEditWindow window = new AddEditWindow();
window.Show();
}
And I want to pass DataContext to the AddEditWindowViewModel. In a Code-Behind, I could done something like this:
private void BtnEdit_Click(object sender, RoutedEventArgs e)
{
AddEditWindow addEditWindow = new AddEditWindow((sender as Button).DataContext as Molds);
addEditWindow.Show();
}
And then retrieve it AddEditWindow like this:
private Molds _currentMold = new Molds();
public GamesEdit(Molds selectedMold)
{
InitializeComponent();
if (selectedMold != null)
{
_currentMold = selectedMold;
}
DataContext = _currentMold;
But in MVVM I can't. So, is there a way to do it without breaking MVVM pattern?
p.s. since I'm new to the MVVM, I would really appreciate detailed explanation.
update:
MainWindowViewModel:
internal class MainWindowViewModel : ViewModel
{
#region Variables
#region Textblocks for search
private Molds newMolds { get; set; } = new Molds();
public string TxtType
{
get => newMolds.Type;
set => newMolds.Type = value;
}
public string TxtName
{
get => newMolds.Name;
set => newMolds.Name = value;
}
public string TxtKus
{
get => newMolds.Kus;
set => newMolds.Kus = value;
}
#endregion
#region AllMolds
private ObservableCollection<Molds> allMolds = new ObservableCollection<Molds>(ApplicationContext.GetContext().Molds.ToList());
public ObservableCollection<Molds> AllMolds
{
get => allMolds;
set => allMolds = value;
}
#endregion
#region FilteredMolds
private ObservableCollection<Molds> filteredMolds = new ObservableCollection<Molds>(ApplicationContext.GetContext().Molds.ToList());
public ObservableCollection<Molds> FilteredMolds
{
get
{
filteredMolds = AllMolds;
var currentfilteredmolds = new List<Molds>(filteredMolds);
if (TxtName != null)
currentfilteredmolds = currentfilteredmolds.Where(p => p.Name.ToLower().Contains(TxtName.ToLower())).ToList();
if (TxtType != null)
currentfilteredmolds = currentfilteredmolds.Where(p => p.Type.ToLower().Contains(TxtType.ToLower())).ToList();
if (TxtKus != null)
currentfilteredmolds = currentfilteredmolds.Where(p => p.Kus.ToLower().Contains(TxtKus.ToLower())).ToList();
return new ObservableCollection<Molds>(currentfilteredmolds);
}
set => filteredMolds = value;
}
#endregion
#endregion
#region Commands
#region CloseApplicationCommand
public ICommand CloseApplicationCommand { get; }
private bool CanCloseApplicationCommandExecute(object p) => true;
private void OnCloseApplicationCommandExecuted(object p)
{
Application.Current.Shutdown();
}
#endregion
#region SearchCommand
public ICommand SearchCommand { get; }
private bool CanSearchCommandExecute(object p) => true;
private void OnSearchCommandExecuted(object p)
{
OnPropertyChanged("FilteredMolds");
}
#endregion
#region Open AddEditWindowCommand
public ICommand AddEditWindowCommand { get; }
private bool CanAddEditWindowCommandExecute(object SelectedRow) => true;
private void OnAddEditWindowCommandExecuted(object SelectedRow)
{
AddEditWindow window = new AddEditWindow();
window.Show();
}
#endregion
#region DeleteMoldCommand
public ICommand DeleteMoldCommand { get; }
private bool CanDeleteMoldCommandExecute(object SelectedItems)
{
if (SelectedItems != null) return true; else return false;
}
private void OnDeleteMoldCommandExecuted(object SelectedItems)
{
System.Collections.IList items = (System.Collections.IList)SelectedItems;
var moldsforRemoving = items?.Cast<Molds>().ToList();
if (MessageBox.Show($"You want to remove the following {moldsforRemoving.Count()} molds?", "Attention",
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
try
{
ApplicationContext.GetContext().Molds.RemoveRange(moldsforRemoving);
ApplicationContext.GetContext().SaveChanges();
MessageBox.Show("Data deleted successfully.", "Data deletion",
MessageBoxButton.OK, MessageBoxImage.Information);
AllMolds = new ObservableCollection<Molds>(ApplicationContext.GetContext().Molds.ToList());
OnPropertyChanged("FilteredMolds");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString(), "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
#endregion
#region DragMoveCommand
public ICommand DragMoveCommand { get; }
private bool CanDragMoveCommandExecute(object p) => true;
private void OnDragMoveCommandExecuted(object p)
{
Application.Current.MainWindow.DragMove();
}
#endregion
#endregion
public MainWindowViewModel()
{
#region Command Samples
CloseApplicationCommand = new LamdaCommand(OnCloseApplicationCommandExecuted, CanCloseApplicationCommandExecute);
SearchCommand = new LamdaCommand(OnSearchCommandExecuted, CanSearchCommandExecute);
AddEditWindowCommand = new LamdaCommand(OnAddEditWindowCommandExecuted, CanAddEditWindowCommandExecute);
DeleteMoldCommand = new LamdaCommand(OnDeleteMoldCommandExecuted, CanDeleteMoldCommandExecute);
DragMoveCommand = new LamdaCommand(OnDragMoveCommandExecuted, CanDragMoveCommandExecute);
#endregion
#region Variable Samples for searching
TxtName = null;
TxtKus = null;
TxtType = null;
#endregion
}
}
AddEditWindowViewModel
internal class AddEditWindowViewModel : ViewModel
{
#region Variables
private Molds _currentMold = new Molds();
#endregion
#region Commands
#region CloseWindowCommand
public ICommand CloseWindowCommand { get; }
private bool CanCloseWindowCommandExecute(object p) => true;
private void OnCloseWindowCommandExecuted(object p)
{
Application.Current.Windows[1].Close();
}
#endregion
#region DragMoveAddEditWindowCommand
public ICommand DragMoveAddEditWindowCommand { get; }
private bool CanDragMoveAddEditWindowCommandExecute(object p) => true;
private void OnDragMoveAddEditWindowCommandExecuted(object p)
{
Application.Current.Windows[1].DragMove();
}
#endregion
#endregion
public AddEditWindowViewModel()
{
#region Command samples
CloseWindowCommand = new LamdaCommand(OnCloseWindowCommandExecuted, CanCloseWindowCommandExecute);
DragMoveAddEditWindowCommand = new LamdaCommand(OnDragMoveAddEditWindowCommandExecuted, CanDragMoveAddEditWindowCommandExecute);
#endregion
}
}
I connect them to the window using <Window.DataContext>:
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
Same goes for the AddEditWindowViewModel.
DataGrid binding:
<DataGrid x:Name="DGridMolds"
AutoGenerateColumns="False"
IsReadOnly="True"
Foreground="White"
BorderBrush="White"
Background="#2b2a38"
Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding Path=FilteredMolds}"
>
AddEditWindow.Xaml:
<Window.DataContext>
<vm:AddEditWindowViewModel/>
</Window.DataContext>
<Border Background="#2f2e3c"
CornerRadius="10">
<Border.InputBindings>
<MouseBinding Command="{Binding DragMoveAddEditWindowCommand}" MouseAction="LeftClick"/>
</Border.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Grid.Row="1" Orientation="Vertical">
<TextBlock Text="Add" FontSize="22" Foreground="White" HorizontalAlignment="Center" Margin="0,30,0,0"/>
<TextBox Foreground="White" Text="{Binding Type, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5, 35, 5, 5" materialDesign:HintAssist.Hint="Type"/>
<TextBox Foreground="White" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5" materialDesign:HintAssist.Hint="Name"/>
<TextBox Foreground="White" Text="{Binding Kus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5" materialDesign:HintAssist.Hint="Kus"/>
In a Code-Behind, I could done something like this:
Take a closer look at your XAML.
You have a binding set in the CommanParameter property.
The binding instance is empty - this means that the binding occurs to the DataContext of the element in which it is specified.
Hence, in the command parameter, you will get this Data Context.
private void OnAddEditWindowCommandExecuted(object SelectedRow)
{
AddEditWindow window = new AddEditWindow()
{DataContext = SelectedRow};
window.Show();
}
So, is there a way to do it without breaking MVVM pattern?
You've already broken MVVM.
ViewModel is not allowed to work with UI elements.
The ViewModel doesn't even need to know what type of View is using it: WPF, Forms, or Console.
And you CREATE the Window!
This is unacceptable for the MVVM concept.
Addition in connection with showing more detailed code in the main question:
I can't understand the logic of your code.
Therefore, I will write in the measure as I understand your intentions.
AddEditWindowViewModel class - designed for logic for editing and/or adding an item.
But then he has to get this element and provide it in his property so that he can create a GUI for editing.
It should be something like this:
namespace MoldsApp
{
public class AddEditWindowViewModel : ViewModel
{
public Molds CurrentMold { get; }
// Constructor called to edit an entity
public AddEditWindowViewModel(Molds currentMold)
{
CurrentMold = currentMold;
}
//Constructor called to create and edit an entity
public AddEditWindowViewModel()
: this(new Molds())
{
}
}
}
Also, your DataContext is set incorrectly.
By creating it inside XAML, you cannot set the data for this instance of the ViewModel.
Therefore, in XAML, you can instantiate a ViewModel used only at the time of its designed.
For a design-time DataContext, use the d: prefix.
<d:Window.DataContext>
<vm:MainWindowViewModel/>
</d:Window.DataContext>
With these changes, the item edit command should be like this:
private void OnAddEditWindowCommandExecuted(object SelectedRow)
{
AddEditWindow window = new AddEditWindow()
{
DataContext = new AddEditWindowViewModel((Molds)SelectedRow)
};
window.Show();
}
There are two views, the first view contains a ListView along with 2 buttons for adding and removing a person. View 2 shows the selected view by using a ContentControl, when the Add button is clicked it shows a form with the name, gender, …, and a button to store the information and display the person name in the ListView.
When I try to remove a person from the ListView it works since the button is within the same view/context of the ListView. But when I call the Add method via AddViewModel it does not update the UI.
Below it is available the relevant parts code of the different classes.
View sample
View1.xaml
<Window.Resources>
<vm:ViewModelBase x:Key="ViewModel"/>
<DataTemplate x:Name="addViewTemplate" DataType="{x:Type vm:AddViewModel}">
<views:AddView/>
</DataTemplate>
</Window.Resources>
<Button Command="{Binding change2addViewCommand,
Source={StaticResource ViewModel}}">
<materialDesign:PackIcon Kind="Plus" Width="12"/>
</Button>
<Button Command="{Binding removeFromListCommand, Source={StaticResource ViewModel}}"
CommandParameter="{Binding ElementName=lvNames, Path=SelectedItem}">
<materialDesign:PackIcon Kind="Delete" Width="12"/>
</Button>
<ListView MinHeight="560"
DataContext="{Binding Source={StaticResource ViewModel}}"
ItemsSource="{Binding}"
x:Name="lvNames" >
</ListView>
<ContentControl x:Name="controllerViews" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4"
Grid.RowSpan="5" Content="{Binding CurrentView, Mode=TwoWay}"/>
ViewModelBase.cs
public class ViewModelBase: ObservableCollection<Person>, INotifyPropertyChanged
{
private ViewModelBase currentView;
public ViewModelBase CurrentView
{
get
{
return currentView;
}
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModelBase()
{
for (int i = 1; i <= 2; i++)
{
Add(new Person()
{
Name = "Person" + i,
Age = i,
Gender = Gender.Female,
Comments = "",
Photo_Path = ""
});
}
change2addViewCommand = new RelayCommand(ExecuteAddCommand, CanExectuteAddCommand);
removeFromListCommand = new RelayCommand(ExecuteRemoveCommand, CanExectuteRemoveCommand);
}
public event PropertyChangedEventHandler PropertyChanged;
public bool CanExectuteAddCommand(object param)
{
return true;
}
public void ExecuteAddCommand(object param)
{
CurrentView = new AddViewModel();
}
public bool CanExectuteRemoveCommand(object param)
{
return true;
}
public void ExecuteRemoveCommand(object param)
{
Person player = param as Person;
Remove(player); //Working
}
protected void AddToListView(Person player)
{
Add(player) // Not Working
}
}
}
AddViewModel.cs
public class AddViewModel: ViewModelBase
{
public RelayCommand addViewCheckCommand { get; set; }
public AddViewModel()
{
addViewCheckCommand = new RelayCommand(ExecuteCheckCommand, CanExecuteCheckCommand);
}
private bool CanExecuteCheckCommand(object param)
{
return true;
}
private void ExecuteCheckCommand(object param)
{
BocciaPlayer player = param as Person;
AddToListView(player); // Inherited method
}
}
I am switching two Views. after message sending from Messenger.Default.Send<Message>(new Message {LoadingIndication="Loaded" },"Token"); It's reciving two message's because it create's OneViewModel two times first time in BinaryMultiViewModel second into OneView. But i need only one message. I can not remove something because in first case it should not switch in second it should show data.
For example
MultiView.cs
namespace Test.ViewModel
{
class BinaryMultiViewModel : ViewModelBase
{
readonly static OneViewModel OneViewModel = new OneViewModel();
readonly static FourViewModel FourViewModel = new FourViewModel();
private ViewModelBase currentMultiViewModel;
public BinaryMultiViewModel()
{
currentMultiViewModel = BinaryMultiViewModel.OneViewModel;
}
public ViewModelBase CurrentMultiViewModel
{
get
{
return currentMultiViewModel;
}
set
{
if (currentMultiViewModel == value)
{
return;
}
currentMultiViewModel = value;
RaisePropertyChanged("CurrentMultiViewModel");
}
}
}
}
MultiView.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Content="{Binding CurrentMultiViewModel}" />
</Grid>
OneViewModel.cs:
namespace Test.ViewModel
{
public class OneViewModel : ViewModelBase
{
public OneViewModel()
{
Messenger.Default.Register<Message>(this,"Token", FromMultiModel);
}
private void FromMultiModel(Message input)
{
MessageBox.Show(input.LoadingIndication);
}
}
}
OneView.cs
namespace Test.Views
{
public partial class OneView : UserControl
{
public OneView()
{
DataContext = new OneViewModel();
InitializeComponent();
}
}
}
App.xaml:
<DataTemplate DataType="{x:Type vm:OneViewModel}">
<views:OneView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:FourViewModel}">
<views:FourView/>
</DataTemplate>
You cannot create OneViewModel in OneView's ctor and assing it to datacontext.
The OneViewModel instance is created in BinaryMultiViewModel and when you set it to ContentControl using databinding and DataTempate with OneView usercontrol is chosen, then OneViewModel is set automatically as datacontext of OneView
just remove the line DataContext = new OneViewModel(); and it should work.
I am working on a silverlight 5 existing application where MVVM approached is followed.
I have created a my own ErrorMessageBox.xaml (childwindow) in View folder and i am in situation where this ErrorMessageBox must be popuped in a class inside Model folder.
And i found that the ErrorMessageBox are not accessible in Model (because it is in View folder).So at last i created one more ErrorMessageBox.xaml inside Model so that it will be used in all
classes in Model folder.
And when i try to popup this child window(ErrorMessageBox.xaml) then it do not pop up. Why it happens and how to Pop up this ErrorMessageBox.xaml inside a function call in a class in Model
folder.
public static void ThisFunctionIsCalledIHaveVerifiedOnDebugging(string message) //it is inside a class in Model folder
{
ConfirmationWindow cfw = new ConfirmationWindow();
cfw.SetMessage("Popup test");
cfw.Show(); //it do not pop up it
}
And ConfirmationWindow.xaml is :
<silvercontrols:ChildWindow x:Class="Model.MessageFolder.ConfirmationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:silvercontrols="clr-namespace:Silverlight.Windows.Controls;assembly=Silverlight.Windows.Controls"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
Title="Message" Width="Auto" Height="Auto" MouseRightButtonDown="ChildWindow_MouseRightButtonDown">
<silvercontrols:ChildWindow.Style>
<StaticResource ResourceKey="MessageBoxStyle"/>
</silvercontrols:ChildWindow.Style>
<Grid x:Name="LayoutRoot" MinWidth="360">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="MessageBox" Margin="10 15 0 0" Height="Auto" FontSize="12" Text="Text" Foreground="White" TextWrapping="Wrap" HorizontalAlignment="Left" />
<StackPanel x:Name="ContentBox" Margin="10 15 0 0" Height="Auto" Orientation="Horizontal"></StackPanel>
<StackPanel Margin="0 0 0 10" Orientation="Horizontal" HorizontalAlignment="Center" Height="45">
<Button x:Name="YesBtn" Content="Yes" Width="82" HorizontalAlignment="Left" VerticalAlignment="Bottom" Style="{StaticResource ButtonStyle_Blue}"/>
<Button x:Name="NoBtn" Content="No" Margin="60 0 0 0" Width="82" HorizontalAlignment="Right" VerticalAlignment="Bottom" Style="{StaticResource ButtonStyle_Blue}"/>
</StackPanel>
</StackPanel>
</Grid>
</silvercontrols:ChildWindow>
and ConfirmationWindow.xaml.cs is :
using System.Windows;
namespace Model.MessageFolder
{
public partial class ConfirmationWindow : Silverlight.Windows.Controls.ChildWindow
{
private bool showBtnClose;
public ConfirmationWindow(bool showBtnClose = false)
{
InitializeComponent();
HasCloseButton = showBtnClose;
this.showBtnClose = showBtnClose;
NoBtn.Click += Close;
}
#region METHODS
public void SetMessage(string message)
{
MessageBox.Text = message;
}
public void AddContent(UIElement elt)
{
ContentBox.Children.Add(elt);
}
#endregion
#region EVENT_HANDLER
public void Close(object sender, RoutedEventArgs e)
{
this.Close();
}
#endregion
private void ChildWindow_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
}
}
Why it is not working? How to make it work ?
First thing is that you should not bring your childwindow class in the models folder because it breaks the MVVM pattern. Instead leave it in your views folder.
What you should do is to show the childwindow from your model's view.
To achieve that you need a way to tell your view when to show the childwindow and what message it should display.
First, in your model create a property ErrorMessage:
public class MyModel : INotifyPropertyChanged
{
...
private string _errorMessage;
public string ErrorMessage
{
private set
{
_errorMessage = value;
OnPropertyChanged("ErrorMessage");
}
get { return _errorMessage;; }
}
...
}
Note: I assume here that your model class implements INotifyPropertyChanged interface but it could be a different implementation.
Then in your view's code behind add a dependency property and bind it to your model's ErrorMessage.
The dependency property's change callback is used to display the childwindow.
This could look like the following:
public partial class MyView : UserControl
{
...
public static readonly DependencyProperty ErrorMessageProperty =
DependencyProperty.Register("ErrorMessage", typeof (string), typeof (MyView),
new PropertyMetadata((o, args) =>
{
// Display childwindow when message is changed
string message = args.NewValue as string;
if(message!=null)
{
ConfirmationWindow cfw = new ConfirmationWindow();
cfw.SetMessage(message);
cfw.Show();
}
}));
public string ErrorMessage
{
get { return (string)GetValue(ErrorMessageProperty); }
private set { SetValue(ErrorMessageProperty, value); }
}
...
public MyModel ViewModel
{
...
set
{
DataContext = value;
Binding binding = new Binding();
binding.Source = value;
binding.Path = new PropertyPath("ErrorMessage");
SetBinding(ErrorMessageProperty, binding);
}
...
}
...
}
Then every time you change the value of ErrorMessage in your model it should show the childwindow.