Set Custom Commands to programmatically created Buttons - c#

I'm new to WPF and am trying to create an interface that builds itself from a XML file. The newly built interface contains different controls like textboxes, labels or buttons. And the last one gave me a headache for days, because every button shall get its own command.
Example XML Code for buttons:
ID="1001" GroupID="1" Control="Button" Content="Apply" Margin="2" Width="60"
And this is the method that defines each button:
public Button Button(XElement xElement, Button newButton)
{
if (xElement.Attribute("Width") != null)
newButton.Width = Convert.ToDouble((xElement.Attribute("Width").Value));
if (xElement.Attribute("Content") != null)
{
string sContent = xElement.Attribute("Content").Value;
//Here gets the button its command
Commands Commands = new Commands();
Commands.setCommand(sContent, newButton);
newButton.Content = sContent;
}
return newButton;
}
The Content Attribute names the button and the function at the same time.
Problem: Basically I am trying to build a commands class that contains all possible commands. Each time a button is created the setCommand(sContent, newButton) method scans the commands class and sets up the matching command to this button via a Switch-Case statement.
And now each time the button is clicked the assigned command should fire.
But is it even possible to do it this way, or am I on the wrong track? Is there an easier solution to do this or am I just missing essentials of command binding?
Any hint is appreciated.

First of all, if you have your own commands defined that you want to bind to Buttons on your UI, then they should be exposed via public properties in your VM or DataContext of your UI.
Then once you have this arrangement in place, you can set the Command on button like below:
Binding binding = new Binding();
binding.Path = new PropertyPath("MyCommand"); //Name of the property in Datacontext
button.SetBinding(Button.CommandProperty, binding);

Related

Xamarin: how do I change the color of a button added in code?

My Xamarin app creates buttons in code. I can change the color of a button I click, as the button is referenced in its Clicked handler's sender argument, but I want to change the color of buttons that I didn't click. The problem is, how do I find the buttons I want to change?
I thought about using FindByName, but Name doesn't appear to be an attribute.
One way I can think of: loop through all of the buttons until I find the one with the StyleId of the desired button. Is there an easier way than that?
when you create a button in code you need to keep a reference to it, like you would with any C# object you want to reference later
Button MyButton = new Button { ... };
MyButton.BackgroundColor = Color.Purple;
if you need to access it from multiple places in your code, you should declare it as a class level variable to that it has scope throughout your class
If you want to find the button created in code behind without XAML, like #Jason said, use the variable name of the Button you created or set it directly.
Button button = new Button { ClassId = "button1", BackgroundColor = Color.Red };
If you want to access a view with FindByName, you need to create the button with x:Name in XAML.
There is an XAML example:
<Button x:Name="button1" ></Button>
And in your code behind you could do something like:
button1.BackgroundColor = Color.Red;
If you need to find a view in xaml for some reason, do this:
var button = this.FindByName<Button>("button1");
button.BackgroundColor = Color.Red;

WPF, command is not executing

I'm trying to create simple snake game in C# using WPF. I have multiple user controls. In the first user control I have a list of players. You pick a player and click a select button. After you click this button second user control is shown. This user control contains button to start a snake game.
The problem is that all user controls are created when you run the application but command that is bind to button that strat a snake game is created after you click the select button. Now if you click the start a snake game button the command is not executed. My question is: Does the command object has to exist before the user control is created or is there a way to notify the user control that command has been created?
you should try to implement the INotifyPropertyChanged interface on your command. It notifies the control about changes of the binded property.
Have a look here, there is an example for the "FirstName" property.
You need to return an actual command, so it must exist, but you can always create it if it doesn't. For example have the property 'getter' make sure the command exists before returning it:
private ICommand myThing;
public ICommand MyThing
{
get
{
if (myThing == null)
{
myThing = new MyCommand(myArgs);
}
return myThing;
}
}
Alternatively if you're running C# 6 then you can initialise the command at the auto-property declaration:
public ICommand MyCommand { get; } = new MyCommand(myArgs);

How to show a View from a ViewModel and also set the data context of that view in WPF with MVVM

How to open a new View(Dialog) from a view model command and also set the new view's data context with its view model. this question is bothering me a lot, although there has been so many question on this but I could not get satisfied with any of the answer so far.
So, suppose:
I have a start up dialog called MainView and I show this dialog and set its data context in App.xaml.cs (OnStartUp) method. In MainView, there is a button called "open a new dialog" and this button's command is bind with a delegate command in MainViewModel. So, when user hits this button, then command calls the execute method.
Let's say command in MainViewModel which is bind with button in view is as following
public ICommand ShowNewDialogCommand
{
if(this._showNewDialogCommand == null)
{
this._showNewDialgoCommand = new DelegateCommand(ShowDialogFromVM);
}
}
private void ShowDialogFromVM()
{
}
And let's say the new dialog that I want to show is ListAllStudentsView and its ViewModel is StudentsViewModel. So, what are the various approaches of showing this dialog without breaching the MVVM pattern? And what are the merits and demerits of each approach?
First, we need to create a view (somewhere) with its datacontext set. Easy enough, we instantiate the view and either pass it the view model (assuming the view sets its data context in its constructor) or set it manually. The view could also have declared the view model in XAML if we so desired.
Method 1:
Window dialog = new ListAllStudentsView(new StudentsViewModel());
Method 2:
Window dialog = new ListAllStudentsView();
dialog.DataContext = new StudentsViewModel();
Method 3:
<Window.DataContext>
<local:StudentsViewModel/>
</Window.DataContext>
Now we need to put this code (and the associated dialog.ShowDialog() somewhere). I see two options, right in the command's execute function, or in the view's code-behind (triggered by an event raised by the command's execute function like "RequestDialog").
I prefer the first, even though it doesn't adhere as rigidly to MVVM because it is a lot simpler, less code, and easier to manage. If you want to be very strict about adhering to MVVM however, I would have the ViewModel raise an event like "RequestDialog" in the command function that the view listens to and runs the constructor and ShowDialog() function.

Event handlers when using a string as a data template for dataform in Silverlight

I am attempting to form some xaml for a dataform programmatically using a string. I can get the combo box to appear. but when I attempt to use the code specifying the "MouseLeftButtonUp" or the "Loaded" event handler in the string; the page will turn white (no noticeable error) out after going into it. Please see relevant code below.
StringBuilder editTemplate = new StringBuilder("");
editTemplate.Append("<DataTemplate ");
editTemplate.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ");
editTemplate.Append("xmlns:toolkit='http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit' ");
editTemplate.Append("xmlns:navigation='clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation' ");
editTemplate.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' >");
editTemplate.Append("<StackPanel>");
editTemplate.Append(#" <toolkit:DataField Label='" + GetFieldWithoutNumber(theInfo, theDataContext) + "'>");
/* Won't Work */ editTemplate.Append(#" <ComboBox MouseLeftButtonUp='ComboBox_MouseLeftButtonUp' />");
/* Will Work */ editTemplate.Append(#" <ComboBox />");
editTemplate.Append(#" </toolkit:DataField>");
editTemplate.Append("</StackPanel></DataTemplate>");
dynamicDataForm.EditTemplate = XamlReader.Load(editTemplate.ToString()) as DataTemplate;
Event handlers hooked up in XAML are required to be declared in the code-behind connected to the XAML file. In the case of a ResourceDictionary or anything loaded from XamlReader.Load there can't be any code-behind, so event handlers can't be set in the XAML. The easiest way to get around this restriction would be to not build your template from strings and just declare it in the Resources section of your XAML file which you can then do like:
Resources["MyTemplate"] as DataTemplate
to get the template and assign it in code like you're doing here, or just use StaticResource in XAML. As long as it stays in the same XAML file connected to this code the event handlers you have in it currently should work fine. The dynamic part of the strings would also need to be changed to use Bindings.
If you want to stick with the XamlReader method you have 2 problems to solve.
Locate the ComboBox instance inside the rendered template
Wait until the template is rendered to look for the ComboBox
To find the ComboBox you need to first give it an x:Name attribute in the template text (you can just replace the event code currently there). Next you need to be able to locate an item in the visual tree by name. This is fairly straightforward and you can find an example here to do that.
To call this code at the right time you either need to override OnApplyTemplate, which unfortunately won't work if you're in something like a UserControl, or use another trick to keep it from running until all the controls are rendered. Here's a full example that could go in a constructor and uses the find method linked from above:
DataTemplate template = Resources["MyTemplate"] as DataTemplate;
dynamicDataForm.ContentTemplate = template;
Dispatcher.BeginInvoke(() =>
{
ComboBox button = FindVisualChildByName<ComboBox>(this, "MyControl");
if (button != null)
button.MouseLeftButtonUp += (s, _) => MessageBox.Show("Click");
});
In your case it looks like your template might need to wait to switch to an edit state before it renders in which case you'd need to hold off on connecting the event and find some other event on your dataform that happens when that state is changed.
One solution is to handle the BeginningEdit event of the DataForm and use that to subscribe your event handler to the ComboBox's MouseLeftButtonUp event.
To do this, add to your code-behind a private field named isEventWiredUp. We'll use this field to keep track of whether we've subscribed to the event and prevent the event from being subscribed to more than once.
Next, add an x:Name="..." attribute to your ComboBox. We use this name to get at the combobox.
Once that is done, add the following two methods, which should do what you want. Replace yourComboBoxName with the x:Name you gave to your combobox:
private void dynamicDataForm_BeginningEdit(object sender, CancelEventArgs e)
{
Dispatcher.BeginInvoke(OnBeginEdit);
}
private void OnBeginEdit()
{
if (!isEventWiredUp)
{
var combobox = dynamicDataForm.FindNameInContent("yourComboBoxName") as ComboBox;
if (combobox != null)
{
combobox.MouseLeftButtonUp += combobox_MouseLeftButtonUp;
isEventWiredUp = true;
}
}
}
Subscribe the first of these two methods to the DataForm's BeginningEdit event.
I have to admit that I was unable to get the MouseLeftButtonUp event to fire on the ComboBox. I'm not sure why this happens, but it seems to be a general problem with the ComboBox as opposed to something that happens because of the way you're constructing XAML. I was able to get an event handler for the ComboBox's SelectionChanged event to work, however.
I also tried replacing the Dispatcher.BeginInvoke line with a direct call to the OnBeginEdit method, but I found that this approach didn't work. The events weren't quite wired up correctly; again, I'm not sure why.
Rather than trying to hookup the event directly, you can use interactivity to bind up your events
e.g.
...
editTemplate.Append("xmlns:i='clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity' ");
...
editTemplate.Append(#"
<ComboBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName='MouseLeftButtonUp'>
<i:InvokeCommandAction Command='{Binding DataContext.YourCommand,
RelativeSource={RelativeSource AncestorType=XXX}}'
CommandParameter='{Binding}'/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>");
you might have to use some ancestor binding to get to the context on which your handler is defined. I use a custom implementation of InvokeCommandAction; basically a copy of System.Windows.Interactivity.InvokeCommandAction but extended so that it will pass the event args to the command, you might want to do the same.
XamlReader.Load not allowed to attach eventHandlers in it.
so use this technique to dynamically attach the eventHandlers to it.
1- Write your Xaml string without eventHandlers -But write the Name property of those Controls.
2- Load the string with XamlReader.Load(str);
3- Then load the content of DataTemplate from it. using Grid template = ((Grid)(dt.LoadContent()));
Note: here Grid is the parent Control in DataTemplate.
4- Find the Control by Name you want to attach the Event Handler.
Button img = (Button)template.FindName("MyButtonInDataTemplate");
I hope it helps.

Two-Way Binding is not working

In my Silverlight project I am creating textboxes which are two-way databound to some Context during runtime. The binding in one direction (from the source to the target) seems to work fine, but the other direction (from the target back to the source) is not showing any effect.
This is the data-context:
public class Leg : INotifyPropertyChanged {
private string passengers;
public string Passengers {
get { return passengers; }
set {
// here I have a breakpoint.
passengers = value;
FirePropertyChanged("Passengers");
}
}
private void FirePropertyChanged (string property) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then on another place I am creating a new TextBox control together with a binding for it:
Binding passengersBinding = new Binding();
// viewModelLeg is an instance of the class Leg from above
passengersBinding.Source = viewModelLeg;
passengersBinding.Path = new PropertyPath("Passengers");
passengersBinding.Mode = BindingMode.TwoWay;
legItem.paxTextBox.SetBinding(TextBox.TextProperty, passengersBinding);
Now when I am altering the value of the Passengers string the corresponding textbox that is bound to it is updating its text correctly. So here's everthing fine.
But when i change the text of a textbox manually and then make the textbox lose its focus, nothing happens - i.e. there is no two-way binding taking place - no down propagation of the new text-value of the textbox to the source !
I have a breakpoint at the setter of the passengers-attribute (marked with the breakpoint-comment above). When I am getting all this right the binding engine also uses this public setter when the target-value of a binding has changed to update the source - so when this happens the breakpoint must be hit. But he doesn't ! So it seems that i can do what I want with my textbox (play with the focus or press enter) it is never updating its source.
Am I overseeing something ? There must be a capital error either in my code or in my thinking.. i would be really thankful for any ideas ...
EDIT:
In the following I try to demonstrate how i create my XAML objects and my DataContext objects. Because I am creating XAML controls and their bindings at runtime I haven't found a good solution to implement the MVVM approach very well. So I am doing the following (which is maybe not the best way to do it):
The situation I am modelling is that I have a UserControl (called LegItem) which is comprised (primarely) of textboxes. At runtime the user can create as much of these userControls as hew wishes to (one after the other).
On my ViewModel side I have a class (called Leg) that serves as a ViewModel for exactly one LegItem. So when I have say n (XAML-) LegItems then I also have n Leg instances. I store these Leg objects in a List.
So I am doing the following everytime the user clicks the 'add a new leg' button:
// here we are inside the applications view in an .xaml.cs file
public void AddLeg () {
// this is going to serve as the ViewModel for the new LegItem
// I am about to create.
Leg leg = viewModel.insertLeg();
// here I am starting to create the visual LegItem. The ViewModel object
// I have created in the previous step is getting along with.
createLegItem(leg);
}
// the primary job here is to bind each contained textbox to its DataContext.
private LegItem createLeg (Leg viewModelLeg) {
// create the visual leg item control element
// which is defined as a XAML UserControl.
LegItem legItem = new LegItem();
Binding passengersBinding = new Binding();
// viewModelLeg is an instance of the class Leg from above
passengersBinding.Source = viewModelLeg;
passengersBinding.Path = new PropertyPath("Passengers");
passengersBinding.Mode = BindingMode.TwoWay;
legItem.paxTextBox.SetBinding(TextBox.TextProperty, passengersBinding);
}
// on the viewModel side there is this simple method that creates one Leg object
// for each LegItem the View is creating and stores inside a simple list.
public Leg InsertLeg () {
Leg leg = new Leg();
legList.add(leg)
return leg;
}
New Answer
Since you mentioned your binding was actually to a custom UserControl and not actually a TextBox, I would suggest looking into the XAML of your UserControl and making sure it is binding the data correctly
Old Answer
I did a quick test with a new Silverlight project and noticed that the startup project is SilverlightApplication1.Web, not SilverlightApplication.
This means that the breakpoint in the setter won't actually get hit when I run the project. You'll notice the breakpoint circle is just the outline, and the color isn't filled in. If you hover over it, it will say
The breakpoint will not currently be hit. No symbols have been loaded
for this document
If I start SilverlightApplication1 instead of the .Web version, the breakpoint gets hit.
The property is getting changed correctly regardless of which version I startup, however the breakpoint isn't getting hit if I start the project with the .Web version. I suspect this is your issue.

Categories