Adding UserControl to a Canvas by a Command in ViewModel in WPF - c#

I have a UserControl say Stock and it has a Button called Display
<Button Command="{Binding DisplayCommand}" CommandParameter="StockGroups">Display</Button>
Now when i Click this button it should add an another UserControl named Display to the Canvas which is in HomeWindow and should pass the CommandParameter to the Display userControl.
private DelegateCommand<string> _displayCommand;
public virtual void DisplayExecuted(string param){}
public ICommand DisplayCommand
{
get
{
if (_displayCommand == null)
_displayCommand = new DelegateCommand<string>(new Action<string>(DisplayExecuted));
return _displayCommand;
}
}

An alternative method which is more MVVM-ish would be to have a boolean property named ShouldDisplayControl, which is then bound to the control's Visibility property (using the [BooleanToVisibilityConverter]) 1), while passing the CommandParameter as a second property, maybe ControlParameter, which the control is also bound to.

This is not an operation that should involve the ViewModel, since it does not manipulate any model data.
Instead of a ViewModel command, consider merely handling the button's OnClick in the code-behind of the xaml.
In your HomeWindow.xaml.cs file:
protected override void Display_OnClick(object sender, EventArgs e) {
var buttonName = ((Button)sender).Name; // gets the name of the button that triggered this event
var displayControl = new DisplayControl(); // your user control
displayControl.param = buttonName; // set the desired property on your display control to the name of the button that was clicked
((Canvas)Content).Children.Add(displayControl); // 'Content' is your Canvas element
}
And in your HomeWindow.xaml file:
<Button x:Name="StockGroups" Click="Display_OnClick" Text="Display" />
That should get you what you want, without needing to create and invoke a command in the viewmodel. The name of the clicked button will be set to the specified property in your userControl, and an instance of the control will be created inside the Canvas.

Related

How to get text from entry

I create an entry using
<Entry Placeholder="Reply..."/>
It is inside a ListView > ItemTemplate > DataTemplate > ViewCell
The thing is I need a way once a user clicks the submit button in that ViewCell it gets the text for the entry in that cell. I am using Binding to set the values so I don't how to get the text.
When you handle the button's click event, assuming you are using an event handler to listen to the Clicked event, you can get the BindingContext of the button (which should also be the same BindingContext for the entire ViewCell).
Like so:
public void OnButtonClicked(object sender, EventArgs e)
{
// Assuming the List bound to the ListView contains "MyObject" objects,
// for example List<MyObject>:
var myObjectBoundToViewCell = (MyObject)((Button)sender).BindingContext;
// and now use myObjectBoundToViewCell to get the text that is bound from the Entry
}
Seeing your code I could notice why the #sme's answer doesn't fit you. You're making a very confusing and poor use of bindings and xaml, and I'm quite sure that move to MVVM is the best thing you can do now.
But, if you insist to keep the code like it is now, you can just add the Reply text bound to the Entry's text property, like that:
<Entry Placeholder="Reply..."
HorizontalOptions="FillAndExpand"
Margin="0, 0, 0, 5"
Text="{Binding Reply}"/>
So, as you are sending the entire MessageObjectobject to the tap command, you'll be able to get the text content just this way:
public void ReplyCommandClick(string id, object msg)
{
MessageObject message = (MessageObject) msg;
message.ShowReplyField = message.ShowReplyField ? false : true;
//var viewcell = (MessageObject)((Label)msg).BindingContext;
//viewcell. // There were no options for the entry
var reply = msg.Reply;
SendReply(id, msg, reply);
}

How to get access to parent's DataContext

how can I get access to a parent's DataContext?
I've got an UserControl containing 3 buttons, which I want to use for several different UserControls - so the user has always the same actions available.
When clicked on button 'Add' I need to do something inside the current DataContext, which isn't much of a hassle since I can just do the following:
public void CtrlClicked(object sender, RoutedEventArgs e){
Button btn = sender as Button;
MyClass2 c2 = btn.DataContext as MyClass2;
c2.CallCustomMethod();
}
When the button 'Del' is clicked I want to delete the object MyClass2 out of a List<MyClass2> which is held in MyClass1.
In order to do that I need to have access to MyClass1.
My UI (pseudo code):
Window (DataContext = base)
Grid
UserControl uc1 (DataContext = base.MyClass1)
Grid
ListView
ListView.DataTemplate
UserControl uc2 (DataContext = base.MyClass1.MyClass2)
Grid
UserControl ucButtons
Grid
UserControl uc2
ListView.DataTempate
ListView.PanelTemplate
UniformGrid
ListView.PanelTemplate
ListView
Grid
UserControl uc1
Grid
Window
So how can I get access to the MyClass1-objext?
I found out that I can walk the tree using .Parent, but can only do that to a certain point:
Grid gScheduleControlBar = btn.Parent as Grid;
UserControl ucScheduleControlBar = gScheduleControlBar.Parent as UserControl;
Grid gDay = ucScheduleControlBar.Parent as Grid;
UserControl ucDay = gDay.Parent as UserControl;
//ucDay.Name confirms it's the userControl defined
Grid grid = ucDay.Parent as Grid;
// grid.Name="" and grid.Parent = null
so from here there is no further way upwards, which means I can't pass the UserControl 'border'.
Any ideas?
As fallback-option there is of course the way of storing a reference of MyClass1 in MyClass2.
EDIT => Final Result:
<Button x:Name="Del" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor AncestorType=UserControl AncestorLevel=3}}"
If you want to do this via Bindings, you can use RelativeSource={RelativeSource Mode=FindAncestor AncestorType=yourNamespace:YourType}, from code you can use the VisualTreeHelper to get the visual parent of any control.
If there are multiple parents of that type in your hierarchy can can additionally specify an AncestorLevel. In the example you included, it looks like AncestorType=UserControl and AncestorLevel=2 should work.

Passing data between user controls in wpf

I have a user control with a button which when clicked opens a new user control.
private void Button_Click(object sender, RoutedEventArgs e)
{
Window window = new Window
{
Title = "Window2",
Content = new UserDataControl2()
};
window.ShowDialog();
}
I need to pass a collection to the new user control. How can I do it?
The easiest way is to create a custom constructor for your user control.
// Button_Click event
Window window = new Window
{
Title = "Window2",
Content = new UserDataControl2("My Data");
};
// User Control class.
string _info;
public UserDataControl2(string info)
{
_info = info.
};
You could also create a method or property in the user control to receive the data as well. Use whichever seems more appropriate in your context.
The best way is passing object to DataContext of this Window. For this you will need to create a class where store this parameters (ViewModels) and after "binding" to the Window (View). After you can pass this object assigning to Datacontext.
Look to MVVM model to understand better what I mean.
MVVM Pattern Made Simple
MVVM in Depth

Bubbling up a button command in UserControl

I have a button in a user control called LauncherView + LauncherViewModel, I add this to the main window called MainView + MainViewModel.
When I click on the button I would like to capture the event in the MainViewModel. How can I do this?
In the LauncherViewModel is easy enough with:
RelayCommand launchCommand;
public ICommand LaunchCommand{
get{
if (launchCommand == null){
launchCommand = new RelayCommand(LaunchCommandExecute, CanLaunchCommandExecute);
}
return launchCommand;
}
}
private void LaunchCommandExecute(object parameter){
//Do something to recognize the button.
//Could use ObservableCollection<Module> module_objects
//to match, if I could get the buttons content or name
}
private bool CanLaunchCommandExecute(object parameter){
return true;
}
However tried this in the MainViewModel as well hoping for a bubbling effect to occur, no such luck unfortunately.
You can do this by using a RoutedCommand instead of a RelayCommand.
Alternatively, your ViewModel could raise an event, to which the parent (MainViewModel) could subscribe. When the button is pressed, you could simply raise the event.
Change LaunchCommand to a dependency property and then bind that to your MainWindow's command in the XAML where you declare the LauncherView.

How do I capture the modification of a DataContext's object's property using MVVM?

I have a WPF page which has a DataContext assigned to it. On this page it has a couple textboxes. Each textbox is bound to a property of a parent object from the DataContext. For instance, the DataContext has a Location object on it. The Location object has properties like "Name" and "Address1" etc.
The textbox controls are bound like this:
<Binding Path="Location.Name" Mode="TwoWay">
I have a "Commit" button that only becomes enabled once all the data is valid. This relies on the evaluation of this process after a property is set. If this wasn't a sub property of Location, I could easily do it like this:
public Location Location
{
get { return _location; }
set
{
_location = value;
OnPropertyChanged("Location");
OnPropertyChanged("IsCommitEnabled");
}
}
But since the Location object is never actually set, but rather the Location object's "Name" property, that event never fires. Is there a way to fire off my "OnPropertyChanged("IsCommitEnabled")" method when a property of my Location object is modified/set?
I'm guessing your button has a Click handler and its Enabled property is bound to IsCommitEnabled. Which is fine except that you are responsible for the update of the enabled state - which is the problem you expressed.
An alternative would be to replace the Click handler and Enabled binding with a binding to a Command.
You can impliment the Command either as a RoutedCommand and set CanExecuteRoutedEventArgs.CanExecute to IsCommitEnable or you could provide an implimentation of ICommand where ICommand.CanExecute will check IsCommitEnabled.
In both these case the framework looks after polling the CanExecute method - so you dont have to do a proptry update when the property changes.
RoutedCommand example:
<Window.CommandBindings>
<CommandBinding
Command="{x:Static p:Window1.StartButtonCommand}"
Executed="buttonStart_Executed"
CanExecute="CommandBinding_StartButtonEnabled" />
</Window.CommandBindings>
public static RoutedCommand StartButtonCommand = new RoutedCommand();
private void CommandBinding_StartButtonEnabled(object sender, CanExecuteRoutedEventArgs e) {
e.CanExecute = ....;
}

Categories