动态元素命令绑定/ INotifyPropertyChanged-TKCustomMap

时间:2018-07-08 22:33:15

标签: c# xamarin binding event-handling inotifypropertychanged

我正在使用TK.CustomMap,在执行需要在View中执行的方法时遇到问题。我想知道这可能是什么原因。起初,我只是在代码中使用MapClicked方法,但现在我正尝试使用Bindable Command属性,但无法确定它是否正在触发。 [EDIT]我无法在XAML中绑定此命令,因为所有元素都动态添加到了后面的代码中。我确实相信命令实际上是在触发,也许INotifyPropertyChanged实现的问题更多,但是我不确定[EDIT]

这是xaml.cs绑定和MapClicked命令

  mapView.SetBinding(TKCustomMap.MapClickedCommandProperty, "MapClickedCommand");

    private void MapView_MapClicked(object sender, TKGenericEventArgs<TK.CustomMap.Position> e)
    {
        var map = sender as TKCustomMap;
        if (map.MapClickedCommand != null)
        {
            map.MapClickedCommand.Execute(e);
        }
    }

在ViewModel中,MapClickedCommand及其支持的方法和对象

public  Command<Position> MapClickedCommand
{
    get
    {
        return new Command<Position>(async (position) =>
        {
            if (!ZoneOpen)
                return;
            ActiveZonePositions.Add(position);

            if (ActiveZonePositions.Count > 1)
            {
                ZonePolyLine.LineCoordinates = ActiveZonePositions;
            }

            var pin = new TKCustomMapPin
            {
                Position = new TK.CustomMap.Position(position.Latitude, position.Longitude),
                IsVisible = true,
                IsDraggable = true,
                ShowCallout = false,
            };
            _pins.Add(pin);
            await CreatePointAsync(position);
            PointCount++;
        });
    }
}

public async Task CreatePointAsync(TK.CustomMap.Position position)
{
    var zone = await RetrieveZoneAsync(ActiveZoneID);
    Model.Point PointToAdd = new Model.Point
    {
        ZoneSys = zone.ZoneSys,
        PointName = "",
        Latitude = position.Latitude,
        Longitude = position.Longitude,
        PointOrder = PointCount + 1
    };
    ActiveZonePoints.Add(PointToAdd);
}

public static List<Position> ActiveZonePositions = new List<Position>();
public static List<Model.Point> ActiveZonePoints = new List<Model.Point>();
    public static int ActiveZoneID;
    public static int ActivePointID;
    public static int PointCount;
    public static bool ZoneOpen = false;

public ObservableCollection<TKCustomMapPin> Pins
{
    get { return _pins; }
    set
    {
        if (_pins != value)
        {
            _pins = value;
            OnPropertyChanged("Pins");
        }
    }
}

有一个OpenNewZone()方法将ZoneOpen设置为true。

这是_pins列表中各种属性更改的侦听器等

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

ObservableCollection<TKCustomMapPin> _pins;

和ViewModel的方法

public MapPageViewModel()
        {
            _pins = new ObservableCollection<TKCustomMapPin>();
        }

所以我在想,随着属性更改为侦听器,它应该在我单击地图后立即在地图上添加一个图钉,但实际上并没有。可能是我缺少有关Bindings的一些知识,但是我觉得我已经阅读了所有文章和很多问题,并且涵盖了所有基础。

有人知道我想念什么吗?

附录: 我也知道地图正在感应点击,因为我在VS的输出中看到了这一点

07-08 18:42:48.711 D/ViewRootImpl@af05362[MainActivity](30715): ViewPostIme pointer 0
07-08 18:42:48.787 D/ViewRootImpl@af05362[MainActivity](30715): ViewPostIme pointer 1

2 个答案:

答案 0 :(得分:0)

这不是设置命令绑定的正确方法。命令绑定通常在xaml中使用。一个对象公开一个ICommand类型的属性,然后在xaml中将Command属性绑定到该ICommand。

对象将声明如下命令:

public class MyViewModel 
{
    public ICommand MyCommand {get;set;}

    public MyViewModel()
    {
       MyCommand = new Command(Command_MyCommand);
    }

    public void Command_MyCommand(object paramter)
    {
       // do something nifty
    }
}

在后面的代码中,将BindingContext设置为MyViewModel的实例。

然后在xaml中,您可以将command属性绑定到命令:

<Button Text="Press me!" Command="{Binding MyCommand}" />

要以编程方式设置命令,只需分配:

SomeControl.Command = MyViewModelInstance.MyCommand;

如果您想在后面的代码中进行操作,订阅该事件将更容易。

**更新:在您的控制下,它看起来像这样:

   mapView.CommandClickedCommand = new Command(Map_ClickedCommand);

   public async void Map_ClickedCommand(object p1)
   {
        var position = p1 as Position; // not sure what the type of position is

        if (!ZoneOpen)
            return;
        ActiveZonePositions.Add(position);

        if (ActiveZonePositions.Count > 1)
        {
            ZonePolyLine.LineCoordinates = ActiveZonePositions;
        }

        var pin = new TKCustomMapPin
        {
            Position = new TK.CustomMap.Position(position.Latitude, position.Longitude),
            IsVisible = true,
            IsDraggable = true,
            ShowCallout = false,
        };
        _pins.Add(pin);
        await CreatePointAsync(position);
        PointCount++;
    };  

答案 1 :(得分:0)

想出了这一点。需要更改我用作命令的方法。我提到的OpenNewZone方法是作为命令绑定的。事实证明,可以通过这种方式绑定命令而不会出现问题。唯一的问题是OpenNewZone命令没有将ZoneOpen bool设置为true,因为它没有触发。我已经将它设置为方法,但是以这种方式设置

public Command<EventArgs> OpenNewZone
    {
        get
        {
            return new Command<EventArgs>(async e =>
            {
                ZoneOpen = true;
                PointCount = 0;
                ActiveZonePositions.Clear();
                Zone ZoneToAdd = new Zone
                {
                    Contactsys = MobileUser.ContactSys,
                    ZoneTypesSys = 1,
                    OrganizationSys = MobileUser.OrganizationSys,
                    ZoneName = ""
                };

                ActiveZoneID = await AddZoneToDBAsync(ZoneToAdd);
                ZoneToAdd.ID = ActiveZoneID;
                ZoneToAdd.ZoneSys = ActiveZoneID;
                await AddZoneToDBAsync(ZoneToAdd);
            });
        }
    }

然后以这种方式进行绑定

Binding openNewZone = new Binding("OpenNewZone");
addZoneButton.SetBinding(Button.CommandProperty, openNewZone);
addZoneButton.Clicked += AddZoneButton_Clicked; 

然后像这样单击事件处理程序

 private void AddZoneButton_Clicked(object sender, EventArgs e)
    {
        var button = sender as Button;
        MapField.Children.Remove(button);

        if (button.Command != null)
        {
            button.Command.Execute(e);
        }

        zoneControls.Orientation = StackOrientation.Horizontal;
        zoneControls.VerticalOptions = LayoutOptions.End;
        zoneControls.HorizontalOptions = LayoutOptions.FillAndExpand;
        zoneControls.HeightRequest = 35;

        MapField.Children.Add(zoneControls);
        zoneControls.Children.Add(saveZoneButton);
        zoneControls.Children.Add(setZonePropsButton);
        zoneControls.Children.Add(deleteZoneButton);
        zoneControls.Children.Add(closeControlsButton);
    }

每次都能完美工作。因此,我最初的问题中的所有内容实际上都是正确的!哦!

但这很棒,因为现在我知道如何在动态添加的元素上完美地编写和绑定命令,同时仍保持MVVM结构。

TK.CustomMap nuget有其自己的Binding属性,因此它们的设置有些不同,但是现在一切都可以正常工作了:)