我的Xamarin XAML页面及其viewmodel存在一些问题。
我已经用一堆方法编写了代码,这些方法是通过XAML的命令执行的。
在功能上它的所有工作都按照需要。但是,方法执行完毕后视图不会刷新。因此,尽管插入内容并从我的可观察集合中删除内容(通过API调用更新),并且有一个notifypropertychanged事件,页面也不会重新加载。
有什么想法吗?
ViewModel (省略了一些属性以符合帖子的字符数限制)
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 PublishCommand { 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);
PublishCommand = new Command<string>(Publish);
LoadResultsCommand = new Command<int>(LoadResults);
MissingFinisherCommand = new Command(MissingFinisher);
MissingTimingCommand = new Command(MissingTiming);
Invalid = false;
AddTimingVisibility = false;
AddScanVisibility = false;
AddVisibility = true;
PublishProvisionalVisibility = false;
PublishFinalVisibility = false;
IsBusy = false;
RaceId = race.Id;
RaceDate = race.RaceDate;
RaceStartTime = Convert.ToDateTime(race.RaceStartTime);
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");
}
}
}
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");
}
}
}
string _aboveBelow;
public string AboveBelow
{
get { return _aboveBelow; }
set
{
if (_aboveBelow == value)
return;
_aboveBelow = value;
OnPropertyChanged("AboveBelow");
}
}
string _aboveBelowPosition;
public string AboveBelowPosition
{
get { return _aboveBelowPosition; }
set
{
if (_aboveBelowPosition == value)
return;
_aboveBelowPosition = value;
OnPropertyChanged("AboveBelowPosition");
}
}
string _addHH;
public string AddHH
{
get { return _addHH; }
set
{
if (_addHH == value)
return;
_addHH = value;
OnPropertyChanged("AddHH");
}
}
string _addMM;
public string AddMM
{
get { return _addMM; }
set
{
if (_addMM == value)
return;
_addMM = value;
OnPropertyChanged("AddMM");
}
}
string _addSS;
public string AddSS
{
get { return _addSS; }
set
{
if (_addSS == value)
return;
_addSS = value;
OnPropertyChanged("AddSS");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
void AddTiming()
{
AddScanVisibility = false;
AddTimingVisibility = !AddTimingVisibility;
}
void AddScan()
{
AddScanVisibility = !AddScanVisibility;
AddTimingVisibility = false;
}
void MissingFinisher()
{
//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
{
var scancount = Scans.Count();
Device.BeginInvokeOnMainThread(() => IsBusy = true);
var IsThereConnection = GlobalFunctions.CheckForInternetConnection();
if (IsThereConnection == false)
throw new Exception("You cannot add a scan whilst you are offline");
else if (string.IsNullOrEmpty(MissingBib))
throw new Exception("You must enter a bib number");
else if (string.IsNullOrEmpty(AboveBelow))
throw new Exception("You must choose if you are adding the bib above or below");
else if (string.IsNullOrEmpty(AboveBelowPosition))
throw new Exception("You must enter the position you are inserting " + AboveBelow);
else if (Convert.ToInt32(AboveBelowPosition) > scancount)
throw new Exception("The position you have chosen to insert above or below does not exist. Please enter a valid value");
else if (Convert.ToInt32(AboveBelowPosition) < 1)
throw new Exception("The position you have chosen to insert above or below does not exist. Please enter a valid value");
//We are good to go
else
{
var InsertedSequence = new int();
var abovebelow = Scans.SingleOrDefault(i => i.Position == Convert.ToInt32(AboveBelowPosition));
var abposition = Convert.ToInt32(AboveBelowPosition);
if (AboveBelow == "Above")
{
InsertedSequence = abovebelow.Sequence + 5;
}
else if (AboveBelow == "Below")
{
InsertedSequence = abovebelow.Sequence - 5;
}
else
{
return;
}
//Need to add resequencing and reallocation of position numbers
var scan = new ScanPost
{
RaceId = RaceId,
BibNumber = MissingBib,
Sequence = InsertedSequence,
Position = abposition,
Reorder = true,
Status = 0
};
var createscan = ScansAPI.CreateScan(scan);
if (createscan.Code != "Created")
throw new Exception("Error creating scan");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
LoadResults(RaceId);
OnPropertyChanged("Results");
}
});
}
void MissingTiming()
{
Task.Run(() =>
{
try
{
var timingcount = Timings.Count();
Device.BeginInvokeOnMainThread(() => IsBusy = true);
var IsThereConnection = GlobalFunctions.CheckForInternetConnection();
if (IsThereConnection == false)
throw new Exception("You cannot add a timing whilst you are offline");
//We are good to go
else
{
if (AddHH == "") { AddHH = "0"; }
if (AddMM == "") { AddMM = "0"; }
if (AddSS == "") { AddSS = "0"; }
var ManualEndTime = RaceStartTime.AddHours(Convert.ToDouble(AddHH))
.AddMinutes(Convert.ToDouble(AddMM))
.AddSeconds(Convert.ToDouble(AddSS));
var timing = new TimingPost
{
RaceId = RaceId,
StartTime = RaceStartTime,
EndTime = ManualEndTime,
Reorder = true,
Position = 0,
Status = 0
};
var createtiming = TimingsAPI.CreateTiming(timing);
if (createtiming.Code != "Created")
throw new Exception("Error creating timing");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
Device.BeginInvokeOnMainThread(() => LoadResults(RaceId));
Device.BeginInvokeOnMainThread(() => OnPropertyChanged("Results"));
}
});
}
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 != "NoContent")
throw new Exception("Error deleting timing");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
LoadResults(RaceId);
OnPropertyChanged("Timings");
Device.BeginInvokeOnMainThread(() => LoadResults(RaceId));
Device.BeginInvokeOnMainThread(() => 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 != "NoContent")
throw new Exception("Error deleting scan");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
LoadResults(RaceId);
OnPropertyChanged("Results");
}
});
}
void Publish(string status)
{
Task.Run(() =>
{
try
{
var publish = new Publish();
publish.RaceId = RaceId;
publish.ResultStatus = status;
Device.BeginInvokeOnMainThread(() => IsBusy = true);
var IsThereConnection = GlobalFunctions.CheckForInternetConnection();
if (IsThereConnection == false)
throw new Exception("You cannot publish results whilst you are offline");
//We are good to go
else
{
var publishrace = RacesAPI.PublishRace(publish);
if (publishrace.Code != "NoContent")
throw new Exception("Error publishing race");
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
ValidationError = ex.Message;
Invalid = true;
});
return;
}
finally
{
Device.BeginInvokeOnMainThread(() => IsBusy = false);
LoadResults(RaceId);
OnPropertyChanged("Results");
}
});
}
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;
if (elapsed.Milliseconds.ToString().Length > 2)
{
elapsedmillis = elapsed.Milliseconds.ToString().Substring(0, 2);
}
else
{
elapsedmillis = elapsed.Milliseconds.ToString();
}
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; }
if((elapsedhours == "00"))
{
result.Elapsed = elapsedminutes + ":" + elapsedseconds + "." + elapsedmillis;
}
else
{
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 = RaceStartTime;
newresult.EndTime = RaceStartTime;
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();
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; }
if ((elapsedhours == "00"))
{
newresult.Elapsed = elapsedminutes + ":" + elapsedseconds + "." + elapsedmillis;
}
else
{
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;
OnPropertyChanged("Results");
}
}
}