-3

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.

1 Answers1

0

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

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116