I have to bind Grid Drop Event and PreviewMouseLeftButtonDown event in ViewModel. I have a RelayCommand. But it is done only for passing the object, I have to pass the routed event by using the command and also for MouseButtonEventArgs. my sample code is as below, please give any suggestion for using the routed event args and MouseButtonEventArgs in viewmodel.
<Grid
x:Name="mainGrid"
AllowDrop="True"
Background="#F0F0F0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventCommandExecuter Command="{Binding GridDrop}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
<Grid Background="LightBlue" PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown">
EventCommandExecuter
public class EventCommandExecuter : TriggerAction<DependencyObject>
{
#region Constructors
public EventCommandExecuter()
: this(CultureInfo.CurrentCulture)
{
}
public EventCommandExecuter(CultureInfo culture)
{
Culture = culture;
}
#endregion
#region Properties
#region Command
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
#region EventArgsConverterParameter
public object EventArgsConverterParameter
{
get { return (object)GetValue(EventArgsConverterParameterProperty); }
set { SetValue(EventArgsConverterParameterProperty, value); }
}
public static readonly DependencyProperty EventArgsConverterParameterProperty =
DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
public IValueConverter EventArgsConverter { get; set; }
public CultureInfo Culture { get; set; }
#endregion
protected override void Invoke(object parameter)
{
var cmd = Command;
if (cmd != null)
{
var param = parameter;
if (EventArgsConverter != null)
{
param = EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.InvariantCulture);
}
if (cmd.CanExecute(param))
{
cmd.Execute(param);
}
}
}
}
I want to pass object and RoutedEventArgs like below in viewmodel. Please help
public void Grid_Drop(object sender, RoutedEventArgs e)
{
}
I feel like commands are often overkill for such simple tasks.
You can simply declare your ViewModel in the code behind of your view like so:
public partial class MainWindow : Window
{
private ViewModel _vm;
public ViewModel Vm
{
get { return _vm;}
set
{
_vm = value ;
}
}
//....Constructor here....
}
Then create a public event :
public event RoutedEventHandler OnGridDrop;
and call it in :
public void Grid_Drop(object sender, RoutedEventArgs e)
{
OnGridDrop?.Invoke(sender,e)
}
Now you only need to initialize your ViewModel:
public MainWindow()
{
InitializeComponent();
Vm = new ViewModel();
OnGridDrop += Vm.OnGridDrop;
}
and subscribe a corrsponding handler that you declared in your ViewModel.
Related
I have a window called SettingsWindow and I have some user controls that can be content of the window. I have a ContentControl and I have a method in view-model that returns new instance of user control to ContentControl's content. I need to set binding properties of user control to view-model programatically.
<Window x:Class="KnitterNotebook.Views.Windows.SettingsWindow"
<Window.Resources>
<viewModels:SettingsViewModel x:Key="SettingsViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource SettingsViewModel}">
<ContentControl Content="{Binding WindowContent, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
public partial class UserSettingsUserControl : UserControl
{
public UserSettingsUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty NewNicknameProperty =
DependencyProperty.Register(nameof(NewNickname), typeof(string), typeof(UserSettingsUserControl),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string NewNickname
{
get { return GetValue(NewNicknameProperty).ToString()!; }
set { SetValue(NewNicknameProperty, value); }
}
public static readonly DependencyProperty ChangeNicknameCommandAsyncProperty =
DependencyProperty.Register(nameof(ChangeNicknameCommandAsync), typeof(ICommand), typeof(UserSettingsUserControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public ICommand ChangeNicknameCommandAsync
{
get { return (GetValue(ChangeNicknameCommandAsyncProperty) as ICommand)!; }
set { SetValue(ChangeNicknameCommandAsyncProperty, value); }
}
}
public class SettingsViewModel : BaseViewModel
{
public SettingsViewModel()
{
WindowContent = new UserSettingsUserControl();
ChooseSettingsWindowContentCommand = new RelayCommand<Type>(ChooseSettingsWindowContent!);
ChangeNicknameCommandAsync = new AsyncRelayCommand(ChangeNicknameAsync);
}
private string newNickname;
public string NewNickname
{
get { return newNickname; }
set { newNickname = value; OnPropertyChanged(); }
}
public ICommand ChooseSettingsWindowContentCommand { get; private set; }
public ICommand ChangeNicknameCommandAsync { get; set; }
private void ChooseSettingsWindowContent(Type userControl)
{
if (userControl == typeof(UserSettingsUserControl))
{
WindowContent = new UserSettingsUserControl()
{
NewNickname = NewNickname,
ChangeNicknameCommandAsync = ChangeNicknameCommandAsync
};
}
}
Please take a look at private void ChooseSettingsWindowContent(Type userControl). When I use Nickname = Nickname etc., the element is not binded to view-model. I need to set binding programatically. I can't create a new instance of user control in window, because I want to return user control from the method. I read about Binding class and BindingOperations but I still can't solve how to implement it. How can I set bindings programatically in ChooseSettingsWindowContent?
I am trying to access property value of a child window's view model from the parent View Model.I am calling window from parent view model.I want to make changes in main window based on the operation in child view model. I couldn't get any value of child view model in parent view model.I am trying this in MVVM pattern.
Interface for dialog
public interface IWindowService
{
void OpenDialogWindow(DialogViewModel vm);
}
Parent view model
public class FunctionalViewModel : INotifyPropertyChanged
{
private readonly IWindowService _windowService;
private string connectionString;
public string ConnectionString
{
get { return connectionString; }
set
{
connectionString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ConnectionString"));
}
}
public FunctionalViewModel(IWindowService windowService)
{
BuildConnectionCommand = new RelayCommand(new Action<object>(BuildConnectionString));
_windowService = windowService;
}
private void BuildConnectionString(object obj)
{
MessageBox.Show("will open a window");
_windowService.OpenDialogWindow(new DialogViewModel());
}
}
Child View Model
public class DialogViewModel : FunctionalViewModel,INotifyPropertyChanged
{
private string textboxsaf;
public string Textboxsaf
{
get { return textboxsaf; }
set {
textboxsaf = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Textboxsaf"));
}
}
private ICommand connectionCommand;
public ICommand ConnectionCommand
{
get { return connectionCommand; }
set { connectionCommand = value; }
}
public DialogViewModel()
{
ConnectionCommand = new RelayCommand(new Action<object>(SetValue));
}
public event PropertyChangedEventHandler PropertyChanged;
public void SetValue(object test)
{
textboxsaf= "ValueFromPopUpWindo";
Application.Current.Windows[1].Close();
}
}
ChildWindow.xaml
<Grid>
<Label x:Name="label" Content="my popup window" HorizontalAlignment="Left" Margin="73,68,0,0" VerticalAlignment="Top" Width="132"/>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="73,121,0,0"
TextWrapping="Wrap"
Text="{Binding Path=Textboxsaf,Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left"
Margin="109,177,0,0" VerticalAlignment="Top" Width="75"
Command="{Binding Path=ConnectionCommand }"
/>
</Grid>
</Window>
MainWindow.xaml
<Grid>
<Button Name="btnConnectionString" Grid.Row="0" Grid.Column="2" Content="Connection string" Height="40" Width="150"
Command="{Binding Path=BuildConnectionCommand}"
DataContext="{Binding tfs}"></Button>
</Grid>
Code behind file of main window
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel()
{
rel = new ReleaseViewModel(),
tfs = new FunctionalViewModel(new WindowService()),
wnd = new DialogViewModel()
};
}
}
public class WindowService : IWindowService
{
public void OpenDialogWindow(DialogViewModel vm)
{
ConnectionWindow win = new ConnectionWindow();
win.DataContext = vm;
win.Show();
}
}
Question
I would like to access the value of the property Textboxsaf in the child view model(DialogViewModel) from parent view model(FunctionalViewModel) . Assign value of Textboxsaf to ConnectionString from the funcitonalviewModel . after closing window is good.
I wouldn't use PropertyChanged to retrieve the value of DialogViewModel.Textboxsaf as this proprty might change multiple times during the lifetime of the dialog.
I would make IWindowService.OpenDialogWindow return a custom DialogResult object or the original DialogViewModel probably converting the IWindowService.OpenDialogWindow to an asynchronous method.
Alternatively implement a IWindowService.DialogClosed event:
FunctionalViewModel.cs
public class FunctionalViewModel : INotifyPropertyChanged
{
private readonly IWindowService _windowService;
private string connectionString;
public string ConnectionString
{
get { return connectionString; }
set
{
connectionString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.ConnectionString)));
}
}
private void BuildConnectionString(object obj)
{
MessageBox.Show("will open a window");
_windowService.DialogClosed += OnDialogClosed;
_windowService.OpenDialogWindow(new DialogViewModel());
}
private void OnDialogClosed(object sender, DialogResultEventArgs e)
{
_windowService.DialogClosed -= OnDialogClosed;
ConnectionString = e.Result.Textboxsaf;
}
}
WindowService.cs
public class WindowService : IWindowService
{
public event EventHandler<DialogResultEventArgs> DialogClosed;
public void OpenDialogWindow(DialogViewModel vm)
{
ConnectionWindow win = new ConnectionWindow();
win.DataContext = vm;
win.Closed += OnConnectionWindowClosed;
win.Show();
}
protected virtual void OnConnectionWindowClosed(object sender, EventArgs e)
{
var dialog = sender as FrameworkElement;
this.DialogClosed?.Invoke(this, new DialogResultEventArgs(dialog.DataContext as DialogViewModel));
}
}
DialogResultEventArgs.cs
public class DialogResultEventArgs : EventArgs
{
public DialogViewModel Result { get; }
public DialogResultEventArgs(DialogViewModel result) => this.Result = result;
}
You could keep a reference to the DialogViewModel and subscribe to its PropertyChanged event:
private void BuildConnectionString(object obj)
{
var childViewModel = new DialogViewModel();
childViewModel.PropertyChanged += OnChildPropertyChanged;
MessageBox.Show("will open a window");
_windowService.OpenDialogWindow(childViewModel);
}
private void OnChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(DialogViewModel.Textboxsaf))
{
childViewModel.PropertyChanged -= OnChildPropertyChanged;
ConnectionString = (sender as DialogViewModel)?.DialogViewModel;
}
}
I have the following textbox
<TextBox Grid.Column="1"
Grid.Row="1"
Name="groupAddressBox"
Width ="80"
Text="{Binding Path=GroupAddress, Converter={StaticResource groupAddressConverter}}"/>
When I change the text manually, it's all good.
But when I try to do this via a button
private void Test_Click(object sender, RoutedEventArgs e)
{
groupAddressBox.Text = "0/0/1";
}
Although the text changes, the source is not updated, and when I click on ok, it recognizes the value that was there before the change.
I cannot upgrade the source straight away, so I prefer to do this this way.
Is there something that can help me force the source upgrade via this way?
Based on your question, I tried to create a Simple Example of MVVM Pattern with very basic functionality. Please do necessary change to XAML and CS file as I took the highlighted code only.
Helper Classes
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class CommandHandler : ICommand
{
public event EventHandler CanExecuteChanged { add { } remove { } }
private Action<object> action;
private bool canExecute;
public CommandHandler(Action<object> action, bool canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute;
}
public void Execute(object parameter)
{
action(parameter);
}
}
ViewModel
public class ViewModel : ViewModelBase
{
private string groupAddress;
public string GroupAddress
{
get
{
return groupAddress;
}
set
{
if(value != groupAddress)
{
groupAddress = value;
OnPropertyChanged("GroupAddress");
}
}
}
public ViewModel()
{
}
private ICommand clickCommand;
public ICommand ClickCommand
{
get
{
return clickCommand ?? (clickCommand = new CommandHandler(() => MyAction(), true));
}
}
public void MyAction()
{
GroupAddress = "New Group Address";
}
}
Window Xaml
<TextBox Grid.Column="1" Grid.Row="1" Width ="80"
Text="{Binding GroupAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Push" Style="{StaticResource TransparentButtonStyle}"
Margin="5" Command="{Binding ClickCommand}"/>
Window Xaml cs
ViewModel vm = new ViewModel();
this.DataContext = vm;
Hi I am following this tutorial,http://blogs.u2u.be/diederik/post/2011/11/14/null.aspx, to bind the visibility of an element to a Boolean property. The program is not working. Here is the code:
<Page.Resources>
<local:BooleanToVisibilityConverter x:Key="TrueToVisibleConverter"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBlock Text=" Hello World"
Visibility="{Binding Path=Show_element, Converter={StaticResource TrueToVisibleConverter}}"/>
<Button Click="Button_Click">press button</Button>
</StackPanel>
</Grid>
public sealed partial class MainPage : Page , INotifyPropertyChanged
{
private bool show_element ;
public bool Show_element
{
get { return show_element; }
set
{
show_element = value;
this.OnPropertyChanged();
Debug.WriteLine("Show_element value changed");
}
}
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Show_element = !Show_element;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged(string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class BooleanToVisibilityConverter : IValueConverter
{
public bool IsReversed { get; set; }
public object Convert(object value, Type typeName, object parameter, string language)
{
var val = System.Convert.ToBoolean(value);
if (this.IsReversed)
{
val = !val;
}
if (val)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The visibility does not change with the property. I was having an error due to intellisense (Error Xaml namespace) which was resolved. Not sure what is wrong with this code.
Thank you.
change
this.OnPropertyChanged();
to
this.OnPropertyChanged("Show_element");
edit:
besides that, you don't have a ViewModel (sorry, missed that when I was checking your code), so you need to create one and set it as DataContext:
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged
{
private bool show_element;
public bool Show_element
{
get { return show_element; }
set
{
show_element = value;
this.OnPropertyChanged("Show_element");
Debug.WriteLine("Show_element value changed");
}
}
public ViewModel()
{
}
public void ButtonClicked()
{
Show_element = !Show_element;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged(string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
and your MainPage.xaml.cs should look somehow like that:
public sealed partial class MainPage : Page
{
private ViewModel _viewModel;
public MainPage()
{
this.InitializeComponent();
_viewModel = new ViewModel();
DataContext = _viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_viewModel.ButtonClicked();
}
}
I want to create master page in mvvm. I created a viewbox that it's name is container for showing my usercontrols and I have two classes, RelayCommand and ViewModel.
Here is my code:
public class ViewModel
{
MainWindow objMainWindow = new MainWindow();
UserControls.History objHistory = new UserControls.History();
UserControls.NewItem objNewItem = new UserControls.NewItem();
UserControls.SideEffect objSideEffect = new UserControls.SideEffect();
public ViewModel()
{
OpenCommand = new RelayCommand(Open);
}
private ICommand openCommand;
public ICommand OpenCommand
{
get { return openCommand; }
set { openCommand = value; }
}
public void Open(object sender)
{
if (sender.ToString() == "btnHistory")
{
objMainWindow.Container.Child = objHistory;
}
if (sender.ToString() == "btnNewItem")
{
}
if (sender.ToString() == "btnSideEffect")
{
}
}
}
And this is my RelayCommand:
public class RelayCommand:ICommand
{
public RelayCommand(Action<object> _action)
{
actionCommand = _action;
}
private Action<object> actionCommand;
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
if (parameter !=null)
{
actionCommand(parameter);
}
else
{
actionCommand("Null");
}
}
}
but when I run solution I faced with NullRefrenceException when it wanted to show my child of container.
I don't know how to make this work.
Your MainWindow instantiates when your program starts. So you shouldn't instantiate it again in your ViewModel (i.e. this line: MainWindow objMainWindow = new MainWindow();). You should use DataBinding instead.
Here is a sample code that gives you an idea:
First define a property of type FrameworkElement in you ViewModel and set it's value to your desired UserControl in the Open method.
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
FrameworkElement _myUc;
public FrameworkElement MyUserControl
{
get
{
return _myUc;
}
set
{
_myUc= value;
OnPropertyChanged("MyUserControl");
}
}
public ViewModel()
{
OpenCommand = new RelayCommand(Open);
}
public void Open(object sender)
{
if (sender.ToString() == "btnHistory")
{
MyUserControl = objHistory;
}
}
// rest of your view model ...
}
Then instantiate your ViewModel as the DataContext of your MainWindow in the Constructor.
MainWindow:
public ViewModel MyViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
MyViewModel = new ViewModel();
DataContext = MyViewModel;
}
And Finally use a ContentControl (instead of ViewBox) [see my note] and bind it's Content to the MyUserControl property of your ViewModel.
XAML:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{Binding MyUserControl}" x:Name="Container"/>
<Button Grid.Row="1" Name="btnHistory" Content="ShowHistory" Command="{Binding OpenCommand}" />
</Grid>
This way each time MyUserControl changes, the ContentControl shows your desired UserControl.
Note that Child property of ViewBox is not a DependencyProperty and thus not bind-able.