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

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

Related

MapHolding action on MapControl doesn't work... a workaround?

It seems that holding method does not work since 2015:
Win10 App - Holding & Releasing the map to manipulate an element on the interface
or simply I don't know how to use it, anyway I need to manage a user holding on PC or mobile of a map.
My map is like a navigator, it follows the user position while it is moving, but when user use gesture to see the map around I have to stop the .center position of the user pushpin.
But I cannot find a way to understand when user move the map. The docs tell to use the holding event
https://learn.microsoft.com/it-it/windows/uwp/maps-and-location/display-maps
but I've tried it on PC and my app doesnt enter in the holding event.
I've tried all the other events, only maptapped and actualcamerachanged works.
but I don't know how to difference from a user move or a map .center move from the app.
Any help?
Thanks!
MapHolding doesn't fire immediately on Pointer down. It fires on a press and long hold after a delay, and only if the map isn't moved. It's intended for things like displaying a context menu (like right-click).
The MapActualCameraChangingEventArgs for the ActualCameraChanging event should tell you if the map moved due to user interaction or due to being changed programmatically.
I resolved with a little of WinMerge and some try
you can find the FocusState.Pointer in sender when the user try to move the map, if not with the map.center you get a FocusState.Unfocused by the sender
private void Mappe_ActualCameraChanged(MapControl sender, MapActualCameraChangedEventArgs args)
{
if (sender.FocusState == FocusState.Pointer)
{
}
}

Mouse clicks C# XNA

This has been bothering me for quite some time so here we go...
I'm making a 2D game using XNA framework and part of the game needs the usage of mouse click funtions in UI. By using these textures as buttons it makes another window to pop up with more functions. Now the problem is when you create a button and use it, it will open another window with more buttons in it. Because these 2 buttons on different windows are at the same location, that one mouse click operates both buttons and makes the action of the last button instantly rather than having to click twice.
The real question is how do I use sprites as buttons rather than checking the position and checking if that possition is clicked?
Thanks in advance for any comments.
I created a basic UI framework that has Windows with Controls on them. Windows are stacked by zOrder as are the controls on each window. There is a ScreenManager class that updates every tick that tracks the mouse. When it sees a button click it walks the window stack from top to bottom looking for the first window that contains the click coordinates. It then calls the OnClick sub of that window instance, which does the same thing for the controls on the window. Each method stops walking the control stack as soon as it finds the first control that contains the click coords.
Each unique window in my game is a subclass of that basic window class that creates child control instances and lays them out in the specific way for it's layout. Controls like buttons raise a clicked event that the window subclass handles to do the logic needed for that button.
You could use a state manager in which the proper UI live.
In your case, the first state would have your first buttons, lets call it FirstGameState. When you click on them, the state machine gets you to a another state, lets call it MenuState. You can implement your state manager in a way that allows you to decide who handle events and who doesn't. The only state that handles your click events would be the one flagged to do so. The rest would only draw and update the visual content but handle no user events.
Here is an example for you:
namespace States
{
class StateManager
{
List<GameState> _states = new List<GameState>();
List<GameState> _statesCopy = new List<GameState>();
public Game Game { get; private set; }
public SpriteBatch SpriteBatch { get; private set; }
public StateManager(Game game, SpriteBatch sb)
{
Game = game;
SpriteBatch = sb;
}
public void AddState(GameState state)
{
_states.Add(state);
}
public void RemoveState(GameState state)
{
_states.Remove(state);
}
public void Update(GameTime gameTime)
{
_statesCopy.Clear();
foreach (GameState state in _states)
if (state.RequireUpdate)
_statesCopy.Add(state);
foreach (GameState state in _statesCopy)
if (state.RequireUpdate)
state.Update(gameTime);
}
public void Draw()
{
foreach (GameState state in _states)
state.Draw();
}
}
}

ZedGraph: Changing behavior of Set Scale to Default

I have plotted few graphs using ZedGraphs. Now, i have an option that user can plot some graphs using different check boxes a can remove them as well. But, i am not scaling the graph when user makes those graphs as i don't want to change the look of the graphs.
Now, if the user zooms and then click to the Set Scale to Default, the graphs got reset as there is a call to AxisChange() i guess.
But, i want the original look of the graphs that i have plotted and not the default view which changes the view completely.
S, is there any way where i can change the behavior of the Set Scale to Default functionality?
You have 2 options to try with,
Get rid off the default context menu item(Set Scale to Default) & add your own custom context menu item.
In-order to remove:
private void zedGraphControl1_ContextMenuBuilder(
ZedGraphControl sender, ContextMenuStrip menuStrip,
Point mousePt, ZedGraphControl.ContextMenuObjectState objState)
{
foreach (ToolStripMenuItem item in menuStrip.Items)
{
if ((string)item.Tag == "set_default")
{
menuStrip.Items.Remove(item);
break;
}
}
}
and to add a new item: http://www.smallguru.com/2009/06/zedgraph-csharp-graph-data-export-to-cs/
Edit the source code (It's a bit tricky but do-able)
I don't see any easy way to change the behavior of the pre-build options.

How to refresh a winform custom control at design time after changing a property

Let's say I create a custom control which embed a trackbar. I also create an orientation property for my custom control.
When I drop the custom control on a form by default it will be horizontal. Then I set it to vertical, the trackbar should refresh to be vertical at design time.
How to do so ?
I think you should call Refresh() after changing the value:
public OrientationProperty Direction
{
get
{
return _direction;
}
set
{
_direction = value;
if (DesignMode)
{
Parent.Refresh(); // Refreshes the client area of the parent control
}
}
}
private OrientationProperty _direction;
Here's my solution to this issue:
1. Whenever you set something property, call Invalidate() in the setter.
2. After correspondent properties and refreshing method (for eg. overridden OnPaint) are implemented, rebuild!!! then you'll see the modifications taken effect in design time
3. During design, always check whether compilation errors are present, as this might stop VS performing all his tasks.
With this, when I put my control on a form, and adjust its own properties, refreshing happens immediately as expected.
PS.: old post, but at least verified the behavior in VS2015 too :)

Cycle Button Background Images in C#

I have a form in C# that has a button that, when clicked, I want the background image to cycle through a set of images (which I have as resources to the project). The images are named '_1', '_2', etc. and each time I click the button I want its background image to increment to the next one and go back to "_1" when it gets to the highest. Is there a way to do this?
I tried getting button1.BackgroundImage.ToString() but that yields System.Drawing.Bitmap instead of Resources._1 like I was thinking it would (in which case I could just get the last character and switch on that to change the background to the appropriate new image).
Thanks for your help.
Why don't you just put the images in an array?
You could subclass Button and override the BackgroundImage property so you can better keep track of the current resource that represents the image. You might also override the onclick method to internally handle cycling to the next image, though that might be a little weird if the resources are handled outside of your derived button class.
class YourClass
{
private IEnumerator<Image> enumerator;
YourClass(IEnumerable<Image> images)
{
enumerator = (from i in Enumerable.Range(0, int.Max)
from image in images
select image).GetEnumerator();
enumerator.MoveNext();
}
public Image CurrentImage { get { return enumerator.Current; } }
public void OnButtonClick() { enumerator.MoveNext(); }
}
You can use this code as a backing class for your control under the assumption that user wont click the button more than two billion times.
Just note that once this class is created you cannot modify given image list outside. If you want to do such things you need to implement disposable pattern and dispose the enumerator accordingly.

Categories