I've been struggling with this issue for a while now, tried all of the answers I have seen so far and I still get the same error message
Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Menu', AncestorLevel='1'.
And my button does nothing
What I'm trying to accomplish
I have this main window called Menu which has a content control and a top bar menu. The idea is that when I press a button from the homeView it changes the mainView to the one selected.
I have already tried this solution WPF MVVM navigate views but got this error message as a result. What I can do for the moment is change views using the top bar on the Menu window but I can not make the childView to execute a command from the parentView
Here's what I have
In my menu window
<Window.DataContext>
<viewModel:MenuPrincipalVistaControlador/>
</Window.DataContext>
<TheThingsInsideMyWindow/>
<ContentControl Grid.Row="1" Margin="0" Content="{Binding vistaActual}"/>
where vistaActual is a reference to a property currentView in my main ViewModel
My data templates
<DataTemplate DataType="{x:Type viewModel:CasaVistaControlador}">
<view:MenuInicioVista/>
</DataTemplate>
<DataTempate DataType="{x:Type viewModel:CajaVistaControlador}">
<view:CajaVista/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:AgregarUsuarioVistaControlador}">
<view:VistaAgregarUsuario/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:AjusteVistaControlador}">
<view:AjustesVista/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:CitaVistaControlador}">
<view:CitaVista/>
</DataTemplate>
Inside of my HomeView
<Button x:Name="btnCitas" Height="150" Width="150" Margin="250,80,0,0" VerticalAlignment="Top" Style="{StaticResource MaterialDesignRaisedButton}" RenderTransformOrigin="0.496,2.246" materialDesign:ButtonAssist.CornerRadius="10" FontFamily="Bahnschrift" FontSize="20" BorderBrush="{x:Null}" Command="{Binding Path=DataContext.CitaVistaComando , RelativeSource={RelativeSource AncestorType={x:Type Menu}}}" />
Classes
Part of me thinks it might be something on my base classes that's making the bind to not work so going from here will be the classes that make everything work
My MainViewModel (datacontext for menu)
class MenuPrincipalVistaControlador: ObservableObject
{
public CasaVistaControlador CasaVista { get; set; }
public CajaVistaControlador CajaVista { get; set; }
public CitaVistaControlador CitaVista { get; set; }
private object _vistaActual;
public RelayCommand CasaVistaComando { get; set; }
public RelayCommand CajaVistaComando { get; set; }
public RelayCommand CitaVistaComando { get; set; }
public object vistaActual
{
get { return _vistaActual; }
set { _vistaActual = value;
OnPropertyChanged();
}
}
public MenuPrincipalVistaControlador()
{
CasaVista = new CasaVistaControlador();
CajaVista = new CajaVistaControlador();
vistaActual = CasaVista;
CasaVistaComando = new RelayCommand(o =>
{
vistaActual = CasaVista;
});
CajaVistaComando = new RelayCommand(o =>
{
vistaActual = CajaVista;
});
CitaVistaComando = new RelayCommand(o =>
{
vistaActual = CitaVista;
});
}
}
My ObservableObject class
class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
My custom RelayCommand class
class RelayCommand :ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand (Action<object> execute,Func<object,bool>canExecute=null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute (object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
Related
I have just started learning WPF yesterday and my goal is to create window with simple grid with hotel booking information. For now there are just room number, number of guests, dates and "Action" columns. In the "Actions" column there is "Save" button. It should be able to save updates or create new booking when clicked in new row. The problem is when I click "Save" button SaveBooking method is not invoked. I'm also not sure how to properly bind to CurrentBooking object. As I am new to WPF I tried to figure it out from few tutorials. Here's what I've created.
XAML:
<Window x:Class="HotelApp.MainWindow"
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:local="clr-namespace:HotelApp"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="1000">
<Grid>
<TabControl>
<TabItem Header="Bookings">
<DataGrid AutoGenerateColumns = "False" ItemsSource="{Binding Bookings}">
<DataGrid.Columns>
<DataGridTextColumn Header = "Room" Binding = "{Binding Room, Mode=TwoWay}" />
<DataGridTextColumn Header = "Floor" Binding = "{Binding NumOfGuests, Mode=TwoWay}" />
<DataGridTextColumn Header = "From" Binding = "{Binding From, Mode=TwoWay}"/>
<DataGridTextColumn Header = "To" Binding = "{Binding To, Mode=TwoWay}"/>
<DataGridTemplateColumn Header = "Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Save" Command="{Binding DataContext.SaveBookingCommand }" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Guests" />
</TabControl>
</Grid>
</Window>
MODEL:
public class BookingModel : ObservableObject
{
private int _room;
public int Room
{
get => _room;
set
{
if (value != _room)
{
_room = value;
OnPropertyChanged("Room");
}
}
}
private int _numOfGuests;
public int NumOfGuests
{
get => _numOfGuests;
set
{
_numOfGuests = value;
OnPropertyChanged("NumOfGuests");
}
}
private DateTime _from;
public DateTime From
{
get => _from;
set
{
_from = value;
OnPropertyChanged("From");
}
}
private DateTime _to;
public DateTime To
{
get => _to;
set
{
_to = value;
OnPropertyChanged("To");
}
}
}
VIEWMODEL:
public class MainWindowVM : ObservableObject
{
private readonly IBookingService _bookingService;
private ICommand _saveBookingCommand;
public ICommand SaveBookingCommand
{
get
{
if (_saveBookingCommand == null)
{
_saveBookingCommand = new RelayCommand(
param => SaveBooking(),
param => (CurrentBooking != null)
);
}
return _saveBookingCommand;
}
}
private ObservableCollection<BookingModel> _Bookings { get; set; }
private BookingModel _currentBookng;
public BookingModel CurrentBooking
{
get { return _currentBookng; }
set
{
if (value != _currentBookng)
{
_currentBookng = value;
OnPropertyChanged("CurrentBooking");
}
}
}
public ObservableCollection<BookingModel> Bookings
{
get { return _Bookings; }
set { _Bookings = value; }
}
public MainWindowVM(IBookingService bookingService)
{
_bookingService = bookingService;
BrowseBookings();
}
public void BrowseBookings()
{
var bookings = _bookingService.Browse().Select(x => new BookingModel { Room = x.Room.RoomId, NumOfGuests = x.NumOfGuests, From = x.From, To = x.To });
Bookings = new ObservableCollection<BookingModel>(bookings);
}
private void SaveBooking()
{
// send CurrentBooking to service
}
}
RelayCommand:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
Your command is in the datacontext of the entire datagrid MainWindowVM.
Your button's datacontext is that of the row - a BookingModel.
You need some relativesource on that binding.
In principle that looks like this:
{Binding DataContext.ParentVMProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
And your type, in this case, will be DataGrid.
You can also bind selecteditem on the datagrid and when they click the button ensure that is selected using the datagrid properties for selection.
Or
You can have a commandparameter on the command which is
CommandParameter="{Binding .}"
Relaycommand usually comes in two flavours one being RelayCommand
Maybe I missed it but I don't see that in your implementation. I'd suggest you go grab the source code for MVVM Light and paste into your solution for a more complete implementation. Or just add the nuget package if you're not using .net core. You want the commandwpf namespace version of relaycommand.
You left out a lot of code, so I don't know which nuget package you used for your ObservableObject. Anywho, I faked the ObservableObject and got the binding working. The main problem was that you were trying to bind SaveBookingCommand at the BookingModel level, when in your code you have it written in the MainWindowVM level.
You can easily fix this by parenting your MainWindowVM in your BookingModel, and change your binding to be Command={Binding Parent.SaveBookingCommand}.
Here's some pointers to the edits that I made:
MainWindow.xaml.cs:
<DataTemplate>
<Button Content="Save" Command="{Binding Parent.SaveBookingCommand}" />
</DataTemplate>
BookingModel.cs:
public class BookingModel : ObservableObject
{
public MainWindowVM Parent { get; private set; }
public BookingModel()
{
this.Parent = null;
}
public BookingModel(MainWindowVM parent)
{
this.Parent = parent;
}
// ... you know the rest
MainWindowVM.cs:
public MainWindowVM : ObservableObject
{
public void BrowseBookings()
{
// NOTICE that I added 'this' as the parameter argument to connect MainWindowVM to the BookingModel.
var bookings = _bookingService.Browse().Select(x => new BookingModel(this) { Room = x.Room, NumOfGuests = x.NumOfGuests, From = x.From, To = x.To });
Bookings = new ObservableCollection<BookingModel>(bookings);
CurrentBooking = Bookings.First();
}
// ... you know the rest
I made a Memory Game with a start menu, and a separate page where the game takes place. On the main menu you can select your theme and press a button to go to the game. I also have a button on the game screen, but I can not figure out how to make it link to the main menu.
I honestly don't know how to go about doing this.
This is the button that links from the main page to the game(in the xaml):
<Button DockPanel.Dock="Top" Padding="25" Click="Play_Clicked" Background="#FF0E0E0E" Foreground="#FFF3FF00" FontSize="18">Start Game</Button>
this is the code in xaml.cs:
private void Play_Clicked(object sender, RoutedEventArgs e)
{
var startMenu = DataContext as StartMenuViewModel;
startMenu.StartNewGame(categoryBox.SelectedIndex);
}
This is the code from "StartMenuViewModel" that contains the "StartNewGame":
public void StartNewGame(int categoryIndex)
{
var category = (SlideCategories)categoryIndex;
GameViewModel newGame = new GameViewModel(category);
_mainWindow.DataContext = newGame;
}
This code works, does anyone know how to make a similar button to go from the game screen to the main menu?
The easiest and most lightweight way opposed to using a Frame, is to create a view model for each page. Then create a main view model which holds all pages and manages their selection. A ContentControl will display the view models using a DataTemplate assigned to the ContentControl.ContentTemplate property or in a multi page scenario either a DataTemplateSelector assigned to ContentControl.ContentTemplateSelector or implicit templates by only defining the DataTemplate.DataType without the Key attribute:
The View
MainWindow.xaml
<Window>
<Window.DataContext>
<MainViewModel x:Key="MainViewModel" />
</Window.DataContext>
<Window.Resources>
<!--
The templates for the view of each page model.
Can be moved to dedicated files.
-->
<DataTemplate DataType="{x:Type PageA}">
<Border Background="Coral">
<TextBlock Text="{Binding Title}" />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type PageB}">
<Border Background="DeepSkyBlue">
<TextBlock Text="{Binding Title}" />
</Border>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Load Page A"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="0" />
<Button Content="Load Page B"
Command="{Binding SelectPageFromIndexCommand}"
CommandParameter="1" />
<!-- The actual page control -->
<ContentControl Content="{Binding SelectedPage}" />
</StackPanel>
</Window>
The View Model
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Pages = new ObservableCollection<IPage>() {new PageA() {Title = "Page A"}, new PageB() {Title = "Page B"}};
// Show startup page
this.SelectedPage = this.Pages.First();
}
// Define the Execute and CanExecute delegates for the command
// and pass to constructor
public ICommand SelectPageFromIndexCommand => new SelectPageCommand(
param => this.SelectedPage = this.Pages.ElementAt(int.Parse(param as string)),
param => int.TryParse(param as string, out int index));
private IPage selectedPage;
public IPage SelectedPage
{
get => this.selectedPage;
set
{
if (object.Equals(value, this.selectedPage))
{
return;
}
this.selectedPage = value;
OnPropertyChanged();
}
}
private ObservableCollection<IPage> pages;
public ObservableCollection<IPage> Pages
{
get => this.pages;
set
{
if (object.Equals(value, this.pages))
{
return;
}
this.pages = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
SelectPageCommand.cs
class SelectPageCommand : ICommand
{
public SelectPageCommand(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
this.ExecuteDelegate = executeDelegate;
this.CanExecuteDelegate = canExecuteDelegate;
}
private Predicate<object> CanExecuteDelegate { get; }
private Action<object> ExecuteDelegate { get; }
#region Implementation of ICommand
public bool CanExecute(object parameter) => this.CanExecuteDelegate?.Invoke(parameter) ?? false;
public void Execute(object parameter) => this.ExecuteDelegate?.Invoke(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
#endregion
}
The Page Models
IPage.cs
// Base type for all pages
interface IPage : INotifyPropertyChanged
{
string Title { get; set; }
}
PageA.cs
// IPage implementation.
// Consider to introduce dedicated interface IPageA which extends IPage
class PageA : IPage
{
public string Title { get; set; }
// Implementation of INotifyPropertyChanged
}
PageB.cs
// IPage implementation.
// Consider to introduce dedicated interface IPageB which extends IPage
class PageB : IPage
{
public string Title { get; set; }
// Implementation of INotifyPropertyChanged
}
I want to remove an item from an ObservableCollection while adhering to MVVM. I understand the task, I think I understand the logic pretty well and have implemented it, but the item is never removed in the view.
I have traced the application with breakpoints and the value of selectedProject is being read correctly. I also added variables to check the Collection size before and after the remove statement, which were the same value so it therefore does not remove the item. My question is why? What have I missed? What rules have I not adhered to? Pretty new to .NET.
**I am using a WCF Service, to return an ObservableCollection of Projects from my CodeFirst DB and this is called as soon as a user opens the Projects view.
View
<ListBox ItemsSource="{Binding ProjectList, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedProject}" SelectedIndex="{Binding ProjectIndex}" BorderThickness="0" Margin="60,195,218.8,212.4">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProjectName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding DeleteCommand}" Content="Up" HorizontalAlignment="Left" Margin="563,195,0,0" VerticalAlignment="Top" Height="35" Width="75"/>
ViewModel
private ObservableCollection<Project> _projectList;
public ObservableCollection<Project> ProjectList
{
get
{
var q = client.ReturnProjects().ToList();
_projectList = new ObservableCollection<Project>(q.ToList());
return _projectList;
}
set
{
_projectList = value;
OnPropertyChanged("ProjectList");
}
public int SelectedProject
{
get { return _selectedProject; }
set
{
_selectedProject = value;
OnPropertyChanged("SelectedProject");
}
}
The method executed by the command is as follows, the command is being hit and the method called.
public void DeleteProject()
{
if (SelectedProject != null)
{
ProjectList.Remove(SelectedProject);
}
}
You need a two-way-binding for the SelectedItem property.
View
<ListBox ItemsSource="{Binding ProjectList}"
SelectedItem="{Binding SelectedProject, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding DeleteCommand}"
Content="Delete"
HorizontalAlignment="Right"
VerticalAlignment="Bottom" />
ViewModel, Model and ICommand Implementation
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
var q = new[] { new Project() { Name = "A" }, new Project() { Name = "B" }, new Project() { Name = "C" } };
ProjectList = new ObservableCollection<Project>(q);
}
private ObservableCollection<Project> _projectList;
public ObservableCollection<Project> ProjectList
{
get
{
return _projectList;
}
set
{
_projectList = value;
OnPropertyChanged("ProjectList");
}
}
Project _selectedProject;
public Project SelectedProject
{
get { return _selectedProject; }
set
{
_selectedProject = value;
OnPropertyChanged("SelectedProject");
}
}
public ICommand DeleteCommand => new SimpleCommand(DeleteProject);
private void DeleteProject()
{
if (SelectedProject != null)
{
ProjectList.Remove(SelectedProject);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Project
{
public string Name { get; set; }
}
public class SimpleCommand : ICommand
{
Action _execute;
public SimpleCommand(Action execute)
{
this._execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => true;
public void Execute(object parameter)
{
_execute();
}
}
I think OnPropertyChanged("ProjectList") needs to be called after deleting the item to raise the notification for updating the view .
The ObservableCollection has to interact with the model layer.
Maybe you need this:
https://blogs.msdn.microsoft.com/bethmassi/2009/05/08/using-the-wpf-observablecollection-with-ef-entities/
I'm currently in the process of mastering the C# WPF MVVM pattern and have stumbled upon a pretty big hurdle...
What I am trying to do fire off a LoginCommand that when successfully executed will allow me to change the parent window's viewmodel. The only issue is I can't quite think of a way to change the parent window's viewmodel without breaking the MVVM design pattern because I can't access the parent window's ContentControl that sets its path to the active UserControlViewModel in the window.
Here's the scenario:
In our App.xaml we have two DataTemplates:
<DataTemplate DataType="{x:Type ViewModels:LoginViewModel}">
<Views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}">
<Views:LoggedView />
</DataTemplate>
In our MainWindow we have:
<ContentControl Content="{Binding ViewModel}" />
The MainWindow code behind will set the ViewModel = LoginViewModel
In our LoginViewModel we have:
<Button Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=pwPasswordBoxControlInXaml}" />
Now for the money... the LoginCommand:
public void Execute(object parameter)
{
// Do some validation
// Async login task stuff
// ...
// Logged in... change the MainWindow's ViewModel to the LoggedInViewModel
}
How can I make the Execute method change the window's viewmodel without breaking the MVVM pattern?
Things I've tried thus far:
Making the MainWindow have a static Instance singleton that I can access and then change the ViewModel property from the command.
Attempting to implement some form of routed command listener in the MainWindow and then have commands fire off routed command events to be handled by the parent window.
I've done a quick demo to show one way of doing it. I've kept it as simple as possible to give the general idea. There are lots of different ways of accomplishing the same thing (e.g. you could hold a reference to MainWindowViewModel inside LoginViewModel, handle everything there then call a method on MainWindowViewModel to trigger the workspace change, or you could use Events/Messages, etc).
Definitely have a read of Navigation with MVVM though. That's a really good introduction that I found helpful when I was getting started with it.
The key thing to take away from this is to have an outer MainWindowViewModel or ApplicationViewModel which handles the navigation, holds references to workspaces, etc. Then the choice of how you interact with this is up to you.
In the code below, I've left out the clutter from defining Window, UserControl, etc. to keep it shorter.
Window:
<DockPanel>
<ContentControl Content="{Binding CurrentWorkspace}"/>
</DockPanel>
MainWindowViewModel (this should be set as the DataContext for the Window):
public class MainWindowViewModel : ObservableObject
{
LoginViewModel loginViewModel = new LoginViewModel();
LoggedInViewModel loggedInViewModel = new LoggedInViewModel();
public MainWindowViewModel()
{
CurrentWorkspace = loginViewModel;
LoginCommand = new RelayCommand((p) => DoLogin());
}
private WorkspaceViewModel currentWorkspace;
public WorkspaceViewModel CurrentWorkspace
{
get { return currentWorkspace; }
set
{
if (currentWorkspace != value)
{
currentWorkspace = value;
OnPropertyChanged();
}
}
}
public ICommand LoginCommand { get; set; }
public void DoLogin()
{
bool isValidated = loginViewModel.Validate();
if (isValidated)
{
CurrentWorkspace = loggedInViewModel;
}
}
}
LoginView:
In this example I'm binding a Button on the LoginView to the LoginCommand on the Window DataContext (i.e. MainWindowViewModel).
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding UserName}"/>
<Button Content="Login" Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.LoginCommand}"/>
</StackPanel>
LoginViewModel:
public class LoginViewModel : WorkspaceViewModel
{
private string userName;
public string UserName
{
get { return userName; }
set
{
if (userName != value)
{
userName = value;
OnPropertyChanged();
}
}
}
public bool Validate()
{
if (UserName == "bob")
{
return true;
}
else
{
return false;
}
}
}
LoggedInView:
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding RestrictedData}"/>
</StackPanel>
LoggedInViewModel:
public class LoggedInViewModel : WorkspaceViewModel
{
private string restrictedData = "Some restricted data";
public string RestrictedData
{
get { return restrictedData; }
set
{
if (restrictedData != value)
{
restrictedData = value;
OnPropertyChanged();
}
}
}
}
WorkspaceViewModel:
public abstract class WorkspaceViewModel : ObservableObject
{
}
Then some other classes you probably already have implemented (or alternatives).
ObservableObject:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
}
RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{ }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return canExecute == null ? true : canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
App.Xaml:
<DataTemplate DataType="{x:Type ViewModels:LoginViewModel}">
<Views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}">
<Views:LoggedInView />
</DataTemplate>
<ContentControl Content="{Binding ViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type vm:LoginViewModelClass}">
<!-- some LoginView -->
</DataTemplate>
<DataTemplate DataType="{x:Type vm:LoggedInViewModelClass}">
<!-- some LoggedInView -->
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
i'm new in WPF and MVVM. I read many articles about WPF commands, but i have still problem with sending value from property text of textbox to ViewModel.
I'm using entity framework code first.
I want to show text from textbox in MessageBox, but when I click to button with command, linked property of viewmodel is null.
Please can you help me?
View- DetailIncidentWindow.xaml
xmlns:wm="clr-namespace:Admin.ViewModels"
<StackPanel>
<StackPanel.DataContext>
<wm:CommentViewModel/>
</StackPanel.DataContext>
<TextBlock Text="Text komentáře:" Style="{StaticResource TextBlockLabel}" Margin="0,10,0,0"/>
<TextBox Name="TextBox_textKomentar" Width="auto" Height="100" TextWrapping="Wrap" Text="{Binding TextKomentar, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="{Binding TextKomentar, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
Ribbon button- DetailIncidentWindow.xaml
<Custom:RibbonGroup.DataContext>
<wm:CommentViewModel/>
</Custom:RibbonGroup.DataContext>
<Custom:RibbonButton
LargeImageSource="..\Shared\img\save_diskete.png"
Label="Show text"
Command="{Binding ButtonCommand}">
</Custom:RibbonButton>
ViewModel- KomentarViewModel.cs
namespace Admin.ViewModels
{
class CommentViewModel:BaseViewModel
{
#region Data
private string textKomentar;
public string TextKomentar
{
get
{
return textKomentar;
}
set
{
textKomentar = value;
OnPropertyChanged("TextKomentar");
}
}
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
OnPropertyChanged("ButtonCommand");
}
}
#endregion
#region Constructor
public CommentViewModel()
{
ButtonCommand = new RelayCommand(new Action<object>(ShowMessage));
}
#endregion
#region Methods
public void ShowMessage(object obj)
{
MessageBox.Show(TextKomentar);
}
#endregion
}
}
Command- RelayCommand.cs
class RelayCommand : ICommand
{
private Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter != null)
{
_action(parameter);
}
else
{
_action("Hello World");
}
}
#endregion
}
You should not create multiple instances of your view model, like you do in
<StackPanel.DataContext>
<wm:CommentViewModel/>
</StackPanel.DataContext>
and
<Custom:RibbonGroup.DataContext>
<wm:CommentViewModel/>
</Custom:RibbonGroup.DataContext>
The value of the DataContext property is inherited by child elements, so you could just set it at the top level, e.g. the Window:
<Window ...>
<Window.DataContext>
<wm:CommentViewModel/>
</Window.DataContext>
...
</Window>