WPF, command is not executing - c#

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);

Related

Swiping images in C# Windows Forms app (Tinder swipe)

I really need to add the possibility of swiping images like in dating apps (Tinder maybe) in my own app. If the image is swiped to the left, then a certain value should be assigned to the variable (for example, +1). If to the right, then nothing should change (+0 to the variable). After swiping the image, the next image should float smoothly (from the front, from the bottom, it doesn't matter).
I tried to do it myself, but there are no ideas how this can be done. I understand that it will be more difficult to do this on Windows Forms than on WPF. I have only recently started to be interested in WPF, so solving this problem on WPF would also be useful, but Windows Forms is still a priority. Please help me solve this issue.
You forgot to define "swipe". Winforms doesn't have the notion of finger input, only the notion of mouse drag.
Do you want, that if the operator drags the mouse to the left that the image moves with it? Is a small drag enough, or should the operator drag the image completely outside the window?
What should happen if the operator drags a small part, but stops dragging? Should the image move back as if there was no drag? Or should the image stay dragged halfway?
Model
You used the word Image, but in fact the images stands for something more: in Tinder it stands for the person behind the image, a name, a birthdate, a description, and other parts, among which an image.
Let's call this a Profile: every profile has several properties, among others an Image.
class Profile
{
public Image Image {get; set;}
...
}
In your model you will need a FIFO sequence of "Profiles to be shown", a collection of rejected Profiles and a collection of accepted Profiles. You didn't say what you wanted to do with the rejected and accepted Profiles, so all I do is put the Rejected Profiles in a Repository, and the accepted ones in a different Repository.
What happens in the repository is hidden for the model. It might be that you delete everything, or you save it in a file, or a database, or whatever, your Model doesn't have to know. All it has to know is that both repositories need to have an interface to put the Profiles in:
interface IProfileRepository
{
void Add (Profile profile);
}
The repository with the rejected images will probably just throw the Profile away, while the other repository might do things like notify the owner of the Profile that he has been accepted.
We also need some input Profiles. I also don't know where they come from:
interface IProfileSource
{
Profile GetProfile(); // returns the next Profile
}
The actual ProfileSource might read the data from an XML file, or from the internet, or whatever, this is outside the question.
So in you program you will have the following:
class ProfileModel
{
private IProfileSource ProfileSource {get;} = new ...;
private IProfileRepository AcceptedProfiles {get;} = new ...;
private IProfileRepository RejectedProfiles {get;} = new ...;
public Profile GetNextProfile()
{
return ProfileSource.GetProfile();
}
public void AcceptProfile(Profile profile)
{
AcceptedProfiles.Add(profile);
}
public void RejectProfile(Profile profile)
{
RejectedProfiles.Add(profile);
}
View
The form that will display the images of the Profile will need a UserControl that will show a Profile. It is hidden what is shown of the Profile. You will probably only show the Image, but if you want, you can let it show the Age of the person, or the Name, Location, etc. All that your program knows is that you can ask the ProfileControl to show a Profile, what is shown, and how, is up to the ProfileControl.
Use visual studio to create a new UserControl, named ProfileControl. Use Visual Studio designer to draw on the control what you want to show when a Profile needs to be shown. If you only want to show the Image, add a PictureBox to the ProfileControl and let it dock. If you also want to show the Name, add a Label, etc
class ProfileControl : UserControl
{
private Profile profile;
public ProfileControl()
{
InitializeComponents();
}
public Profile Profile
{
get => this.profile;
set
{
if (this.Profile != value)
{
this.profile = value;
this.pictureBox1.Image = this.profile.Image;
}
}
}
}
Consider to add an event ProfileChanged and a protected method OnProfileChanged, to notify others that this ProfileControl shows a new Image.
You will need another UserControl that will do the dragging of the ProfileControl. It will have two ProfileControls: the current one and the next one. Upon MouseDrag the location of the current ProfileControl and the next ProfileControl will change. The next ProfileControl will be adjacent to the current one, depending on the direction of the drag.
This SwipeControl hides how the swiping is done. Users of the SwipeControl (= software, not operator), will only set the current and the next Profile, and it gets notified whenever the current profile is accepted or rejected via events. The event will automatically set the Next profile (if there is one)
Use the visual studio designer to give the SwipeControl two ProfileControls. Add events handlers for events:
MouseDown: remember current mouse position as DragStartPosition. Give CurrentProfileControl and NextProfileControl the size of the ClientArea of the SwipeControl. Set the Location of the CurrentProfileControl to (0, 0), so it is in the upper left corner of the ClientArea of the SwipeControl. NextProfileControl is still not visible, we don't know whether the operator will swipe to the left or to the right.
MouseMove: the horizontal distance that the mouse travelled = current mouse position X - DragStartPosition X. Shift the X location CurrentProfileControl with this Distance travelled. Decide whether NextProfileControl should be on the left or on the right side of CurrentProfileControl. Calculate the Location. Make NextProfileControl visible.
MouseUp: If Distance Travelled is more than some minimal, then set the swipe complete, otherwise undo: dock current and make next invisible.
SwipeComplete: if Accepted raise event ProfileAccepted, if Rejected raise event ProfileRejected. The Profile in the NextProfileControl is set to CurrentProfileControl. Fetch the NextProfile and put it in the NextProfileControl
class SwipeControl : CustomControl
{
public Profile CurrentProfile
{
get => this.CurrentProfileControl.Profile;
set => this.CurrentProfileControl.Profile = value;
}
public Profile NextProfile
{
get => this.NextProfileControl.Profile;
set => this.NextProfileControl.Profile = value;
}
public event EventHandler ProfileAccepted;
public event EventHandler ProfileRejected;
protected virtual void OnProfileAccepted()
{
// raise event ProfileAccepted
this.ProfileAccepted?.Invoke(this, EventArgs.Empty);
}
Use visual studio designer to add the event handlers and implement the code as written.
##The SwipeForm##
Use visual studio designer to add the SwipeControl to the SwipeForm. Also add the Model.
Subscribe to the Accepted / Rejected events of the SwipeControl.
Upon load of the form: get the first and the next Profile from the model and put them in the SwipeControl
Upon event ProfileAccepted: get the CurrentProfile from the SwipeControl and put it in the model as Accepted. The nextProfile will be the current one. Get the next from the model and set this as next profile in the SwipeControl.
Do something similar with event ProfileRejected

How do I use a relaycommand on startup and how do I do it if the method I want to pass is within another class

I am interested in learning more about MVVM. I have taken a look at the MVVM Demo App. I understand many of the major concepts behind it.
When I began playing with the app, I wanted to open one of the tabviews by default upon the app starting up. However I am unsure on how to do that.
In the app, I think I understand that when a control panel button is clicked (e.g. View All Customers), the commandrelay creates a new AllCustomersViewModel and the data template applies the view to the viewmodel, the new workspace is created to the Workspaces collection and the tab opens because of the databinding in the main window.
I have no idea how to start this process other than clicking the hyperlink. I know that I need to call new RelayCommand(param => this.ShowAllCustomers()) but I don't understand how to call this without any user interaction, or how to call it from outside of the mainwindowviewmodel, e.g. from the app's onstartup method.
Can someone please advise on the best way to use a relaycommand on the start up of an app? Also, how do I use a relaycommand if the method I want to pass is within another class?
VMaleev has correctly given me a method to call the command, however the example provided was specific to a the collection of commands. What if I have a method Public ICommand HelpPageCommand which creates a new command based on a ShowHelpPage method where ShowHelpPage is;
HelpViewModel workspace = new HelpViewModel();
this.Workspaces.Add(workspace);
this.SetActiveWorkspace(workspace);
How would I call this command then?
- Simple, if the method is ICommand, then simply method.execute(null)
I suppose, you are talking about this article.
To call RelayCommand without user interaction, you just should write:
If want to call from MainWindowViewModel (for example, in constructor):
_commands.FirstOrDefault(q => q.DisplayName == Strings.MainWindowViewModel_Command_ViewAllCustomers).Command.Execute(null);
If want to call from App.xaml.cs (on application startup, code is taken from example and only one line added) or something else place where you have access to view model instance:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
// Create the ViewModel to which
// the main window binds.
string path = "Data/customers.xml";
var viewModel = new MainWindowViewModel(path);
// When the ViewModel asks to be closed,
// close the window.
EventHandler handler = null;
handler = delegate
{
viewModel.RequestClose -= handler;
window.Close();
};
viewModel.RequestClose += handler;
// Allow all controls in the window to
// bind to the ViewModel by setting the
// DataContext, which propagates down
// the element tree.
window.DataContext = viewModel;
// the following line is added
viewModel.Commands.FirstOrDefault(q => q.DisplayName == Strings.MainWindowViewModel_Command_ViewAllCustomers).Command.Execute(null);
window.Show();
}
If the method you want to pass is in another class, there are two ways to do it:
pub/sub mechanism (by using c# events)
have access from every instance of view model to all instances of your view models. In this case you are able to pass method of any instance of view model as parameter of RelayCommand
Hope, it helps

Create property of UserControl Instances (not new)

I have a usercontrol where I want a property that can list all the other instances of the same usercontrols in the Windows Form.
Eg. I have a simple usercontrol (sidebarbutton). I drag-drop 2 instances of it in a UserForm. Now I want a property (in the usercontrol itself) that can list both of them.
I have written this property. However, when used in Property Browser Window of Visual Studio, it allows me to add new instances of sidebarButton control.
private List<SidebarButton> _sidebarButtons;
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always), Category("Roshan")]
public List<SidebarButton> SidebarButtons
{
get { return this._sidebarButtons; }
set { this._sidebarButtons = value; }
}
How to add the 2 instances that I drag-droped in the form in this property.
I know the property needs to be modified but don't have a proper direction to take. Please HELP me.
One way of doing this could be that upon addition of the user control to the form, the user control notifies all other user controls of that type that it now exists. Your user control can implement an interface like so:
public interface INotifiable
{
public void AddToList(INotifiable newButton);
}
When the user control is added to the form you iterate through all the controls and check whether they implement this interface. If they do you call the AddToList method and pass the newly added control to it.

Set Custom Commands to programmatically created Buttons

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);

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