我有一个Xamarin XAML内容页面,上面有一堆按钮。目的是让这些按钮执行我当前已编码到ViewModel中的命令。
问题是,命令没有触发。我在我的void方法中设置了断点,这些断点一直链接到我的构造函数。构造函数正常工作。当我单击该按钮时,断点不会在执行该过程的任务中被点击。
viewmodel的绑定工作正常,因为其他数据绑定到页面OK。
具体来说,void方法DeleteScan和DeleteTiming是我试图首先工作的方法。
顺便说一下,我有类似的代码在另一个视图中成功运行,但我无法解决这个视图/ viewmodel的错误。
有什么想法吗?
XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TechsportiseApp.Views.ResultsProcess" x:Name="ParentView" Title="Processing">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout HorizontalOptions="FillAndExpand" BackgroundColor="Red" Padding="2" IsVisible="{Binding Error}">
<Label Text="{Binding ErrorMessage}" BackgroundColor="Red" TextColor="White" FontAttributes="Bold" FontSize="Small" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="Center" />
</StackLayout>
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" Color="#80000000"/>
<Label Text="Validation messages can go here" TextColor="Red" FontSize="Small" HorizontalOptions="FillAndExpand" HorizontalTextAlignment="Center" IsVisible="{Binding Invalid}" />
<StackLayout Orientation="Horizontal" Padding="3" IsVisible = "{Binding AddVisibility}">
<Button x:Name="buttonAddScan" Style="{StaticResource Buttons}" Image="addmanual.png" Text="Add Finisher" IsVisible="{Binding AddVisibility}" HorizontalOptions="StartAndExpand" />
<Button x:Name="buttonAddTime" Style="{StaticResource Buttons}" Image="addmanual.png" Text="Add Time" IsVisible="{Binding AddVisibility}" HorizontalOptions="EndAndExpand" />
</StackLayout>
<StackLayout Padding="5, 1" Orientation="Horizontal" IsVisible="{Binding AddScanVisibility}" HorizontalOptions="EndAndExpand" VerticalOptions="Center">
<Label Text="Add Missing Finisher" FontAttributes="Bold" VerticalTextAlignment="Center" HorizontalTextAlignment="Start" FontSize="Small" />
<Picker x:Name="pickerAboveBelow" Title="Pick" SelectedItem="{Binding AboveBelow}">
<Picker.Items>
<x:String>Above</x:String>
<x:String>Below</x:String>
</Picker.Items>
</Picker>
<Entry x:Name="entryAboveBelowPosition" Text="{Binding AboveBelowPosition}" HorizontalTextAlignment="Center" Placeholder="Pos" PlaceholderColor="Silver" Keyboard="Numeric" WidthRequest="70" HorizontalOptions="Start" />
<Entry x:Name="entryMissingBib" Text="{Binding MissingBib}" HorizontalTextAlignment="Center" Placeholder="Bib" PlaceholderColor="Silver" Keyboard="Numeric" WidthRequest="70" HorizontalOptions="Start" />
<Button Command="{Binding MissingFinisherCommand}" CommandParameter="{x:Reference entryMissingBib}" Image="addmanual.png" WidthRequest="50" HeightRequest="50" HorizontalOptions="End" />
</StackLayout>
<StackLayout Padding="5, 1" Orientation="Horizontal" IsVisible="{Binding AddScanVisibility}" HorizontalOptions="EndAndExpand" VerticalOptions="Center">
<Label Margin = "3" Text="Add Manual Time" FontAttributes="Bold" VerticalTextAlignment="Center" HorizontalTextAlignment="Start" FontSize="Small" IsVisible="{Binding SubmitVisibility}"/>
<Entry Margin = "3" Text="{Binding AddHH}" HorizontalTextAlignment="Center" Placeholder="HH" PlaceholderColor="Silver" IsVisible="{Binding SubmitVisibility}" Keyboard="Numeric" WidthRequest="50" HorizontalOptions="Start"/>
<Label Margin = "3" Text=":" HorizontalTextAlignment="Center" IsVisible="{Binding EndVisibility}" HorizontalOptions="Start"/>
<Entry Margin = "3" Text="{Binding AddMM}" HorizontalTextAlignment="Center" Placeholder="MM" PlaceholderColor="Silver" IsVisible="{Binding SubmitVisibility}" Keyboard="Numeric" WidthRequest="50" HorizontalOptions="Start"/>
<Label Margin = "3" Text=":" HorizontalTextAlignment="Center" IsVisible="{Binding EndVisibility}" HorizontalOptions="Start"/>
<Entry Margin = "3" Text="{Binding AddSS}" HorizontalTextAlignment="Center" Placeholder="SS" PlaceholderColor="Silver" IsVisible="{Binding SubmitVisibility}" Keyboard="Numeric" WidthRequest="50" HorizontalOptions="Start"/>
<Button Command="{Binding ManualTimeCommand}" IsVisible="{Binding SubmitVisibility}" Image="addmanual.png" WidthRequest="50" HeightRequest="50" HorizontalOptions="End" Style="{StaticResource Buttons}" />
</StackLayout>
<ListView ItemsSource="{Binding Results}" SeparatorVisibility="None" HasUnevenRows="true" IsVisible="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<StackLayout Orientation="Vertical" Grid.Row="0" Grid.Column = "0" Grid.RowSpan="3" Margin="0,0,0,5">
<Label Text="{Binding ScanPosition}" FontSize="Large" FontAttributes="Bold" Margin="0" HorizontalOptions="Center" VerticalOptions="Start" TextColor="Red" />
<Button Image="trash.png" WidthRequest="30" HeightRequest="30" HorizontalOptions="Center" Command="{Binding DeleteScanCommand}" CommandParameter="{Binding ScanId}" />
</StackLayout>
<Label Text="{Binding FormattedName}" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="2" Margin="0,0,0,5" FontSize="Large" />
<Label Text="{Binding EntryBibNumber}" Grid.Column="1" Grid.Row="1" Margin="0,0,0,5" VerticalTextAlignment="Center" HorizontalTextAlignment="Start" FontSize="Large" TextColor="Teal" />
<Label Text="{Binding FormattedClub}" Grid.Column="1" Grid.Row="2" Margin="0" FontSize="Small" TextColor="Gray" HorizontalOptions="StartAndExpand" />
<Label Text="{Binding Category}" Grid.Column="2" Grid.Row="2" Margin="0" FontSize="Small" TextColor="Gray" HorizontalOptions="End" />
<StackLayout Orientation="Vertical" Grid.Row="0" Grid.Column = "3" Grid.RowSpan="3" Margin="0,0,0,5">
<Label Text="{Binding Elapsed}" HorizontalTextAlignment="End" FontSize="Large" FontAttributes="Bold" TextColor="Red" HorizontalOptions="Center" VerticalOptions="Start" />
<Button Image="trash.png" WidthRequest="30" HeightRequest="30" VerticalOptions="Center" HorizontalOptions="Center" Command="{Binding DeleteTimingCommand}" CommandParameter="{Binding TimingId}" />
</StackLayout>
<BoxView HeightRequest="1" Color="#f4da9e" HorizontalOptions="FillAndExpand" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Horizontal" Padding="3">
<Button x:Name="buttonSubmitProvisional" Text="Publish Provisional" IsVisible="true" HorizontalOptions="StartAndExpand" Command="{Binding DeleteScanCommand}" CommandParameter="1"/>
<Button x:Name="buttonSubmitFinal" Text="Publish Final" IsVisible="true" HorizontalOptions="EndAndExpand" />
</StackLayout>
</StackLayout>
</ContentPage>
代码背后
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TechsportiseApp.API;
using TechsportiseApp.Views;
using TechsportiseApp.ViewModels;
using TechsportiseApp.Models;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TechsportiseApp.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ResultsProcess : ContentPage
{
public ResultsProcess(Race race)
{
InitializeComponent();
var viewModel = new ResultsProcessViewModel(race);
BindingContext = viewModel;
ToolbarItems.Add(new ToolbarItem("Help", "help.png", async () =>
{
await Navigation.PushAsync(new ResultsProcessHelp());
}, 0, 2));
}
}
}
ViewModel (删除了一些不重要的部分包含属性)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Timers;
using System.Windows.Input;
using TechsportiseApp.API;
using TechsportiseApp.Models;
using Xamarin.Forms;
using System.Threading.Tasks;
using TechsportiseApp.Helpers;
namespace TechsportiseApp.ViewModels
{
public class ResultsProcessViewModel : INotifyPropertyChanged
{
public ICommand AddTimingCommand { get; set; }
public ICommand AddScanCommand { get; set; }
public ICommand DeleteTimingCommand { get; set; }
public ICommand DeleteScanCommand { get; set; }
public ICommand PublishProvisionalCommand { get; set; }
public ICommand PublishFinalCommand { get; set; }
public ICommand LoadResultsCommand { get; set; }
public ICommand MissingFinisherCommand { get; set; }
public ICommand MissingTimingCommand { get; set; }
public int RaceId { get; set; }
public DateTime RaceDate { get; set; }
//public ResultsViewModel(Race race)
public ResultsProcessViewModel(Race race)
{
AddTimingCommand = new Command(AddTiming);
AddScanCommand = new Command(AddScan);
DeleteTimingCommand = new Command<int>(DeleteTiming);
DeleteScanCommand = new Command<int>(DeleteScan);
PublishProvisionalCommand = new Command<int>(PublishProvisional);
PublishFinalCommand = new Command<int>(PublishFinal);
LoadResultsCommand = new Command<int>(LoadResults);
MissingFinisherCommand = new Command<int>(MissingFinisher);
MissingTimingCommand = new Command<int>(MissingTiming);
Invalid = false;
AddTimingVisibility = false;
AddScanVisibility = false;
AddVisibility = true;
PublishProvisionalVisibility = false;
PublishFinalVisibility = false;
IsBusy = false;
RaceId = race.Id;
RaceDate = race.RaceDate;
Scans = ScansAPI.GetScans(RaceId);
Timings = TimingsAPI.GetTimings(RaceId);
Entries = EntriesAPI.GetEntries(RaceId);
LoadResults(RaceId);
}
ObservableCollection<Result> _results;
public ObservableCollection<Result> Results
{
get
{
return _results;
}
set
{
if (_results != value)
{
_results = value;
OnPropertyChanged("Results");
}
}
}
ObservableCollection<Scan> _scans;
public ObservableCollection<Scan> Scans
{
get
{
return _scans;
}
set
{
if (_scans != value)
{
_scans = value;
OnPropertyChanged("Scans");
}
}
}
ObservableCollection<Timing> _timings;
public ObservableCollection<Timing> Timings
{
get
{
return _timings;
}
set
{
if (_timings != value)
{
_timings = value;
OnPropertyChanged("Timings");
}
}
}
ObservableCollection<RaceEntry> _entries;
public ObservableCollection<RaceEntry> Entries
{
get
{
return _entries;
}
set
{
if (_entries != value)
{
_entries = value;
OnPropertyChanged("Entries");
}
}
}
bool _invalid;
public bool Invalid
{
get { return _invalid; }
set
{
if (_invalid == value)
return;
_invalid = value;
OnPropertyChanged("Invalid");
}
}
string _validationError;
public string ValidationError
{
get { return _validationError; }
set
{
if (_validationError == value)
return;
_validationError = value;
OnPropertyChanged("ValidationError");
}
}
string _errorMessage;
public string ErrorMessage
{
get { return _errorMessage; }
set
{
if (_errorMessage == value)
return;
_errorMessage = value;
OnPropertyChanged("ErrorMessage");
}
}
public Race SelectedRace { get; set; }
private bool _raceCountZero;
public bool RaceCountZero
{
get { return _raceCountZero; }
set
{
if (_raceCountZero == value)
return;
_raceCountZero = value;
OnPropertyChanged("RaceCountZero");
}
}
ObservableCollection<Race> _races;
public ObservableCollection<Race> Races
{
get
{
try
{
var racelist = RacesAPI.GetRaces();
var sortedracelist = new ObservableCollection<Race>(racelist.OrderBy(c => c.Name));
var racecount = racelist.Count();
if (racecount == 0)
{
RaceCountZero = true;
}
else
{
RaceCountZero = false;
}
return racelist;
}
catch
{
Invalid = true;
ValidationError = "Error getting Race List. No internet connection available.";
}
return _races;
}
set
{
if (_races != value)
{
_races = value;
OnPropertyChanged("Races");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
void AddTiming()
{
//Do stuff when adding a timing. It needs to do all this in the observable collection. Then when we submit, we'll update all scans, timings etc with whats in the collection
}
void AddScan()
{
//Do stuff when adding a timing. It needs to do all this in the observable collection. Then when we submit, we'll update all scans, timings etc with whats in the collection
}
void DeleteTiming(int timingid)
{
//Do stuff when deleting a timing. It needs to do all this in the observable collection. Then when we submit, we'll update all scans, timings etc with whats in the collection
Task.Run(() =>
{
try
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
var IsThereConnection = GlobalFunctions.CheckForInternetConnection();
if (IsThereConnection == false)
throw new Exception("You cannot delete a timing whilst you are offline");
//We are good to go
else
{
var deletetiming = TimingsAPI.DeleteTiming(timingid);
if(deletetiming.Code != "200")
throw new Exception("Error deleting timing");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
OnPropertyChanged("Timings");
}
});
}
void DeleteScan(int scanid)
{
//Do stuff when deleting a scan. It needs to do all this in the observable collection. Then when we submit, we'll update all scans, timings etc with whats in the collection
Task.Run(() =>
{
try
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
var IsThereConnection = GlobalFunctions.CheckForInternetConnection();
if (IsThereConnection == false)
throw new Exception("You cannot delete a scan whilst you are offline");
//We are good to go
else
{
var deletetiming = TimingsAPI.DeleteTiming(scanid);
if (deletetiming.Code != "200")
throw new Exception("Error deleting scan");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
OnPropertyChanged("Scans");
}
});
}
void PublishProvisional(int raceid)
{
//Do stuff when submitting provisional. It needs to do all this in the observable collection. Then when we submit, we'll update all scans, timings etc with whats in the collection
}
void PublishFinal(int raceid)
{
//Do stuff when submitting final. It needs to do all this in the observable collection. Then when we submit, we'll update all scans, timings etc with whats in the collection
}
void MissingFinisher(int raceid)
{
}
void MissingTiming(int raceid)
{
}
void LoadResults(int raceid)
{
var results = new ObservableCollection<Result>();
//Start with the timings
foreach(Timing timing in Timings)
{
var result = new Result();
//Basic details
result.RaceId = RaceId;
result.RaceDate = RaceDate;
result.Status = "Processing";
//Timing Data
result.TimingId = timing.Id;
result.TimingPosition = timing.Position;
result.TimingStatus = timing.Status;
result.StartTime = timing.StartTime;
result.EndTime = timing.EndTime;
var elapsed = result.EndTime - result.StartTime;
string elapsedhours = elapsed.Hours.ToString();
string elapsedminutes = elapsed.Minutes.ToString();
string elapsedseconds = elapsed.Seconds.ToString();
string elapsedmillis = elapsed.Milliseconds.ToString().Substring(0,2);
if (elapsedhours.Length==1) { elapsedhours = "0" + elapsedhours; }
if (elapsedminutes.Length == 1) { elapsedminutes = "0" + elapsedminutes; }
if (elapsedseconds.Length == 1) { elapsedseconds = "0" + elapsedseconds; }
if (elapsedmillis.Length == 1) { elapsedmillis = "0" + elapsedmillis; }
result.Elapsed = elapsedhours + ":" + elapsedminutes + ":" + elapsedseconds + "." + elapsedmillis;
results.Add(result);
}
//Add in the scans
foreach (Result result1 in results)
{
var scan = Scans.FirstOrDefault(p => p.Position == result1.TimingPosition);
if(scan != null)
{
result1.ScanId = scan.Id;
result1.ScanPosition = scan.Position;
result1.ScanStatus = scan.Status;
result1.ScanBibNumber = scan.BibNumber;
}
else
{
result1.ScanId = 0;
result1.ScanPosition = 0;
result1.ScanStatus = 99;
result1.ScanBibNumber = "UNKNOWN";
}
}
//Add any scans which there are no times for (Higher than count)
var timingscount = Timings.Count();
var notimescans = new ObservableCollection<Scan>();
foreach (Scan scan in Scans)
{
if (scan.Position > timingscount)
{
var newresult = new Result();
newresult.RaceId = RaceId;
newresult.RaceDate = RaceDate;
newresult.Status = "Processing";
newresult.ScanId = scan.Id;
newresult.ScanPosition = scan.Position;
newresult.ScanStatus = scan.Status;
newresult.ScanBibNumber = scan.BibNumber;
newresult.TimingId = 0;
newresult.TimingPosition = 99999;
newresult.TimingStatus = 99;
newresult.StartTime = DateTime.Now;
newresult.EndTime = newresult.StartTime;
var elapsed = newresult.EndTime - newresult.StartTime;
string elapsedhours = elapsed.Hours.ToString();
string elapsedminutes = elapsed.Minutes.ToString();
string elapsedseconds = elapsed.Seconds.ToString();
string elapsedmillis = elapsed.Milliseconds.ToString();
newresult.Elapsed = elapsedhours + ":" + elapsedminutes + ":" + elapsedseconds + ":" + elapsedmillis;
results.Add(newresult);
}
}
//Then add in the entries
foreach (Result result2 in results)
{
var entry = Entries.FirstOrDefault(p => p.BibNumber == result2.ScanBibNumber);
if (entry != null)
{
result2.EntryId = entry.Id;
result2.FirstName = entry.FirstName;
result2.LastName = entry.LastName;
result2.FormattedName = entry.FirstName + " " + entry.LastName.ToUpper();
result2.Gender = entry.Gender;
result2.DateOfBirth = entry.DateOfBirth;
result2.Club = entry.Club;
result2.Team = entry.Team;
result2.EntryBibNumber = entry.BibNumber;
}
else
{
result2.EntryId = 0;
result2.FirstName = "Unknown";
result2.LastName = "ATHLETE";
result2.Gender = "Unknown";
result2.DateOfBirth = DateTime.Now;
result2.Club = "";
result2.Team = "";
result2.EntryBibNumber = "Unknown";
}
}
var sortedresults = new ObservableCollection<Result>(results.OrderBy(c => c.TimingPosition));
Results = sortedresults;
}
}
}