I must confess I have problems understanding the way wpf works. I have a usercontrol BottomControl nested in my Mainwindow. If a Button in BottomControl is clicked I want certain changes in my Mainwindow (changing content of a textbox for example) to occur.
The easy thing to do is obviously to just call a public procedure in the Click_Event but this is not quite elegant. I got so far as to use RoutedCommands.
public static readonly RoutedCommand BottomGridReSize = new RoutedCommand();
In XAML of Usercontrol
<Button Style="{StaticResource TransparentButton}" Command="{x:Static local:Commands.BottomGridReSize}" >
In Code of MainWindow
void BottomGridReSize_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
void BottomGridReSize_Executed(object sender, ExecutedRoutedEventArgs e)
{
\\Do some stuff
}
Obviously Mainwindow can't use these events because ist doesn't recognize them. What am I missing?
Any help would very much be appreciated
Jon
just for my understanding: you have a BottomControl with a Button and you when the Button is clicked there should something happen in your mainwindow. so why not simply create a DependencyProperty of type ICommand in your BottomControl and bind this to the Button. if you do this you can simply bind a Command of your MainViewmodel to this DP.
<uc:BottomControl MyDPCommand="{Binding MyMainViewmodelCommand}" />
Related
I have a button with its Click event on the code-behind, something like this:
private void ButtonManager_OnClick(object sender, RoutedEventArgs e)
{
var addButton = sender as FrameworkElement;
}
In case I move to MVVM and no code-behind, I use a Command class with Execute, something like this:
public override void Execute(object? parameter)
{
// sender?
}
How should I manage the "sender as.." that was used in code-behind?
You can use CommandParameter to bind the control to itself.
but it's not a good idea to send the ViewComponent to ViewModel and change it. because in MVVM, the View shouldn't know anything about View. If you want to change anything in View, you can define properties in ViewModel and bind them to view separately.
Any way, the solution for your question is in the code below but I don't suggest it:
Sample Code
Xaml:
<Button Command="{Binding Command}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=.}"/>
ViewModel:
private void Execute(object? obj)
{
var sender = obj as FrameworkElement;
}
I'm currently learning how MVVM works and gettings a bit confused.
What I Have Now: I've got a MainWindow.xaml and have made a button that adds in UserControl1.xaml adding it to a ContentControl, which all works great. I've got a folder named ViewModels with a class named SettingsViewModel.cs and another folder named Views with a UserControl named SettingsView.xaml
What I'm trying to figure out: In the User Control I'll have things like buttons, checkboxes, and some other stuff, I want to be able to have a button press in the MainWindow to call a method where I can do stuff like changing the visibility of items among other things. How I go about calling this method from the MainWindow and where to put the method [SettingsViewModels.cs or SettingsView.xaml].
I'm still very new to programming so I'm probability leaving out a bunch of info, so ask me any question.
I have accually got this to work the other way around; calling a method in MainWindow from a UserControl like this...
//this is in the UserControl
private void Button1_Click(object sender, RoutedEventArgs e)
{
MainWindow callMethod = (MainWindow)Application.Current.MainWindow;
callMethod.MyMethod1();
}
//this is in the MainWindow
pubic void MyMethod1()
{
//whatevery i want here
}
There are a couple of things to consider. In MVVM, View communicate to ViewModel through bindings and ViewModel communicate to the View through events typical from INotifyPropertyChanged and ICollectionChanged. Buttons should be binded to a property of type ICommand. The ViewModel should not know about WPF control stuff like Visibility etc.
To change visibility you use an IValueConverter called BooleanToVisiblityConverter.
Without quite understanding what you are asking, here is some pseudo code of how I would do it.
The structure of your files doesn't matter, but dividing them into Views and ViewModels is a good idea.
Disclaimer: This code will not run, shows only the concept. I left Visual Studio on my other computer.
ViewModel:
public class MainWindowViewModel
{
public ICommand OpenCommand { get; }
public object Child { get; private set; }
public MainWindowViewModel()
{
OpenCommand = new RelayCommand(Open);
}
private void DoOpen()
{
Child = new ChildViewModel();
}
}
public class ChildViewModel
{
public bool ShowSomething { get; }
}
public class Program
{
private void SomeStartupLogic()
{
var window = new MainWindow();
windows.DataContext = new MainWindowViewModel(); // or use an IoC container
window.Show();
}
}
View
<Window class="MainWindow">
<Window.Resources>
<DataTemplate DataType="{x:Type ChildViewModel}">
<ChildView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding Child}"/>
<Button Command="{Binding OpenCommand}"/>
</Grid>
</Window>
<UserControl class="ChildView">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConvert"/>
</UserControl.Resources>
<Grid>
<TextBlock Text="Something" Visibility="{Binding ShowSomething, Converter={StaticResource BooleanToVisibilityConvert}/>
</Grid>
</UserControl>
Links
MVVM
Commands
PropertyChanged
I've a UserControl which is the content of a ContentControl. In the UserControl is a Button, which Command property is bound to my VM. The DataContext is set in the ContentControl. I got not binding errors.
Unfortunately the command won't execute by clicking on the Button. When I write a unclean workaround in code-behind it would work.
Maybe have anyone a idea how to fix it. Thanks!
UserControl XAML:
<!-- Button in UserControl -->
<Button Content="PrismCommand" Command="{Binding PrismCmd}" PreviewMouseDown="Button_PreviewMouseDown"/>
UserControl code-behind:
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//this would fire the command
Button bt = sender as Button;
t.Command.Execute(null);
}
VM:
public DelegateCommand PrismCmd { get; private set; }
public MainViewModel()
{
PrismCmd = new DelegateCommand(PrismTest);
}
private void PrismTest()
{
MessageBox.Show("Execute");
}
I've two windows: Main Window, Log Window. How can I update the listbox in the Log Window when some action is happened in the Main Window (e.g. button is clicked)?
Below is the code for listbox in Log Window:
<ListBox x:Name="DebugLogLb" BorderBrush="{x:Null}">
<TextBlock x:Name="DebugLogTb" Text="{Binding LogText}" Background="{x:Null}" />
</ListBox>
When the button in the Main Window is clicked, it will update the listbox. I tried with the code below but it doesn't work.
private void Btn1_Click(object sender, RoutedEventArgs e)
{
var log = new LogWindow();
log.DebugLogLb.Items.Add(new { LogText = "Button 1 is clicked" });
}
I'm able to update the listbox if I put everything in the same window, but I failed to do so with two windows.
My expected output would be like:
Even if both windows are opened, when the buttons in the Main Window are clicked, it will directly update in the Log Window as well.
Thanks for any helps in advanced.
It's hard to tell where you are going wrong without seeing more of the code. This is an example that works. It creates a new LogWindow in the MainWindow ctor and sets the DataContext. When the button is clicked the handler calls show on the window. The ListBox's itemssource property is bound to an ObservableCollection of strings. So any adds/removes are automatically updated on the UI.
LogWindows xaml
<Window x:Class="WpfApplication7.LogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LogWindow" Height="300" Width="300">
<Grid>
<ListBox x:Name="DebugLogLb" BorderBrush="{x:Null}" ItemsSource="{Binding LogText}" />
</Grid>
MainWindow code-behind
public partial class MainWindow : Window
{
LogWindow _logWindow;
public MainWindow()
{
InitializeComponent();
LogText = new ObservableCollection<string>();
_logWindow = new LogWindow();
_logWindow.DataContext = this;
_logWindow.Closed += _logWindow_Closed;
}
private void _logWindow_Closed(object sender, EventArgs e)
{
_logWindow = new LogWindow();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_logWindow.Show();
LogText.Add("Button1 Clicked");
}
public ObservableCollection<string> LogText { get; set; }
}
I have inherited a Sketchflow-prototyped WPF application. I am in the process of adding functionality to the UI.
As it currently is, navigation from screen to screen is defined in XAML as an event as follows:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<pi:NavigateToScreenAction TargetScreen="WpfPrototype1Screens.Screen_1_2"/>
</i:EventTrigger>
</i:Interaction.Triggers>
This works, but the problem is that it doesn't allow me to validate user input before navigating to the new screen.
What I would like to know, is how can I programmatically navigate to the new screen from within the button's click event handler in C#?
For instance:
private void Button1_Click_1(object sender, RoutedEventArgs e)
{
if (userInputValid)
{
NavigateToScreen_1_2();
}
}
This strongly depends on how your next screen is used. If it is a UserControl (View) that is inserted in another part of the application lets say an ContentControl then you can just change the content of the Content control to your new screen.
Take a look at Commands in WPF in combination with MVVM. This can help you with your problem. Instead of having an OnClick event you define an execute function inside your Views ViewModel and have a public property which you can bind to your buttons Command property
like this.
Have a simple class:
public class MainViewModel
{
public MainViewModel()
{
NextViewCommand = new DelegateCommand(NextView);
}
public ICommand NextViewCommand {get;set;}
public UserControl NewView {get;set;}
public void NextView()
{
NewView = new UserControl() //set your NewView property to your new usercontrol
//Apply validation on all your binded properties you want to validate.
}
}
DelegateCommands
Wire up the ViewModel to your View using:
this.DataContext = new MainViewModel();
And connect your command to your button using:
<Button content="Next view" Command="{Binding NextViewCommand}"/>
I think this is the best way to go, and helps you.