UIElement Reference

90 Views Asked by At

I'm currently rewriting a Joystick Usercontrol from the old typical codebehind usercontrol into a MVPVM WinUI 3 and having a problem referencing a UIElement from the presenter class. Yes I know you aren't supposed to reference the UI from the presenter. Just need help on how to rewrite it. The ControlArea in the Presenter Class is where I'm having trouble with. Can someone tell me how to rewrite these two lines?

The View UI code is:

<Ellipse
    x:Name="ControlArea" 
    Height="{x:Bind p.ViewModel.OuterDiameter}" 
    Width="{x:Bind p.ViewModel.OuterDiameter}"
    Fill="{x:Bind p.ViewModel.OuterFill}"
    Stroke="{x:Bind p.ViewModel.OuterStroke}"/>

The Presenter Class Method is:

void OnPointerMoved(object sender, PointerRoutedEventArgs eventArgs)
{
    if (!ViewModel.ControllerPressed) return;

    Double x = eventArgs.GetCurrentPoint(ControlArea).Position.X - ViewModel.ControlRadius;
    Double y = eventArgs.GetCurrentPoint(ControlArea).Position.Y - ViewModel.ControlRadius;

    double disp = Math.Sqrt(x * x + y * y);
    if (disp < ViewModel.ControlRadius)
    {
        ViewModel.JoystickTransform.X = ViewModel.XValue = x;
        ViewModel.JoystickTransform.Y = ViewModel.YValue = y;
    }
    else
    {
        ViewModel.JoystickTransform.X = ViewModel.XValue = ViewModel.ControlRadius * (x / disp);       //A*cos(x)
        ViewModel.JoystickTransform.Y = ViewModel.YValue = ViewModel.ControlRadius * (y / disp);       //A*sin(x)
    }

    ViewModel.OnJoyStickMoved?.Invoke(sender, new EventArgs());
}
2

There are 2 best solutions below

4
Andrew KeepCoding On

This is an example of how to make an Ellipse follow the mouse.

<Grid RowDefinitions="Auto,*">
    <StackPanel Grid.Row="0">
        <TextBlock x:Name="PointerX" />
        <TextBlock x:Name="PointerY" />
    </StackPanel>
    <Canvas
        x:Name="Map"
        Grid.Row="1"
        Background="Transparent"
        PointerMoved="Map_PointerMoved">
        <Ellipse
            x:Name="ControlArea"
            Width="10"
            Height="10"
            Fill="SkyBlue" />
    </Canvas>
</Grid>
private void Map_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    var position = e.GetCurrentPoint(this.Map).Position;
    this.PointerX.Text = position.X.ToString();
    this.PointerY.Text = position.Y.ToString();
    Canvas.SetLeft(this.ControlArea, position.X);
    Canvas.SetTop(this.ControlArea, position.Y);
}

Make sure to set the Canvas's Background to get pointer events.

UPDATE

You can also use bindings.

<Ellipse
    x:Name="ControlArea"
    Canvas.Left="{x:Bind ViewModel.XPosition, Mode=OneWay}"
    Canvas.Top="{x:Bind ViewModel.YPosition, Mode=OneWay}"
    Width="10"
    Height="10"
    Fill="SkyBlue" />
1
Keyboard_Kowboy On

So the only answer I can think of (but I have no idea if this is correct). Is changing the View's Ellipse to:

 <Ellipse x:Name="ControlArea" 
             DataContext="{x:Bind p.ViewModel.ControlAreaEllipse}"
             Height="{x:Bind p.ViewModel.OuterDiameter}" 
             Width="{x:Bind p.ViewModel.OuterDiameter}"
             Fill="{x:Bind p.ViewModel.OuterFill}"
             Stroke="{x:Bind p.ViewModel.OuterStroke}"/>

Changing the Presenter's method to:

    void OnPointerMoved(object sender, PointerRoutedEventArgs eventArgs)
{
    if (!ViewModel.ControllerPressed) return;

    Double x = eventArgs.GetCurrentPoint(ViewModel.ControlAreaEllipse).Position.X - ViewModel.ControlRadius;
    Double y = eventArgs.GetCurrentPoint(ViewModel.ControlAreaEllipse).Position.Y - ViewModel.ControlRadius;

    double disp = Math.Sqrt(x * x + y * y);
    if (disp < ViewModel.ControlRadius)
    {
        ViewModel.JoystickTransform.X = ViewModel.XValue = x;
        ViewModel.JoystickTransform.Y = ViewModel.YValue = y;
    }
    else
    {
        ViewModel.JoystickTransform.X = ViewModel.XValue = ViewModel.ControlRadius * (x / disp);       //A*cos(x)
        ViewModel.JoystickTransform.Y = ViewModel.YValue = ViewModel.ControlRadius * (y / disp);       //A*sin(x)
    }

    ViewModel.OnJoyStickMoved?.Invoke(sender, new EventArgs());
}

and adding to the ViewModel encapsulation by adding:

public Ellipse ControlAreaEllipse { get; set; }

Can someone please tell if me if this is suitable?