自动生成航点和飞机按方阵生成

限制卫星地图放大最大为19级
飞行航线和实际航线默认不显示,增加显示选项
可导入某些步骤
This commit is contained in:
pxzleo 2017-03-15 23:17:14 +08:00
parent cd3f894449
commit 84659afdab
12 changed files with 331 additions and 40 deletions

View File

@ -201,15 +201,46 @@ namespace Plane.FormationCreator.Formation
if (nullableCenter == null) return; if (nullableCenter == null) return;
var center = nullableCenter.Value; var center = nullableCenter.Value;
var newTask = new FlightTask(FlightTaskType.FlyTo); var newTask = new FlightTask(FlightTaskType.FlyTo);
int coptindex = 0;
int colnum = 5; //自动生成列数=4
float coldis = 5;//列相距5米
float rowdis = 5;//行相距5米
float matrixdis = 20; //生成方阵距离30米
int currcol = 0; //当前列号
int currrow = 0; //当前行
Tuple<double,double> colLatLng = new Tuple<double, double>(0, 0);
Tuple<double, double> targetLatLng = new Tuple<double, double>(0, 0);
FlightTaskSingleCopterInfo lastSingleCopterInfo =null;
foreach (var copter in copters) foreach (var copter in copters)
{ {
var lastSingleCopterInfo = lastTask.SingleCopterInfos.Find(info => info.Copter == copter); if (coptindex == 0)
var direction = GeographyUtils.CalcDirection2D(center.Lat, center.Lng, lastSingleCopterInfo.TargetLat, lastSingleCopterInfo.TargetLng); {
var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(lastSingleCopterInfo.TargetLat, lastSingleCopterInfo.TargetLng, (float)GeographyUtils.RadToDeg(direction), 5); lastSingleCopterInfo = lastTask.SingleCopterInfos.Find(info => info.Copter == copter);
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForFlyToTask(copter, targetLatLng.Item1, targetLatLng.Item2, lastSingleCopterInfo.TargetAlt,true); targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(lastSingleCopterInfo.TargetLat, lastSingleCopterInfo.TargetLng, 0, matrixdis);
colLatLng = targetLatLng;
}
else
{
if (currcol < colnum)
targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(colLatLng.Item1, colLatLng.Item2, 90, currcol* coldis);
else
{
currrow++;
currcol = 0;
targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(colLatLng.Item1, colLatLng.Item2, 180, rowdis);
colLatLng = targetLatLng;
}
}
currcol++;
coptindex++;
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForFlyToTask(copter, targetLatLng.Item1, targetLatLng.Item2, lastSingleCopterInfo.TargetAlt, true);
newSingleCopterInfo.TargetHeading = lastSingleCopterInfo.TargetHeading; newSingleCopterInfo.TargetHeading = lastSingleCopterInfo.TargetHeading;
newSingleCopterInfo.CenterDirectionDeg = lastSingleCopterInfo.TargetHeading; newSingleCopterInfo.CenterDirectionDeg = lastSingleCopterInfo.TargetHeading;
newTask.SingleCopterInfos.Add(newSingleCopterInfo); newTask.SingleCopterInfos.Add(newSingleCopterInfo);
} }
Tasks.Add(newTask); Tasks.Add(newTask);
try try
@ -633,6 +664,49 @@ namespace Plane.FormationCreator.Formation
return JsonConvert.SerializeObject(tasks); return JsonConvert.SerializeObject(tasks);
} }
public void ImportTasksindex(string tasksText,int startindex,int endindex)
{
dynamic tasks = JsonConvert.DeserializeObject(tasksText);
var copters = _copterManager.Copters;
int i=1;
foreach (var task in tasks)
{
if ((i >= startindex)&& (i <= endindex))
{
switch ((FlightTaskType)task.type)
{
case FlightTaskType.TakeOff:
// AddTakeOffTask(copters); // added by ZJF
// TakeOffNumAttr = task.takeoffnumber;
break;
case FlightTaskType.FlyTo:
RestoreFlyToTask((bool)task.staggerRoutes, task.singleCopterInfos);
break;
case FlightTaskType.Turn:
RestoreTurnTask(task.singleCopterInfos);
break;
case FlightTaskType.Circle:
RestoreCircleTask(task.singleCopterInfos);
break;
case FlightTaskType.Loiter:
RestoreLoiterTimeTask((float)task.loiterTimeAttr, (bool)task.flashCheck, (float)task.flashCheckPeriod,
(bool)task.oneByOneCheck, (float)task.oneByOneCheckPeriod, (string)task.flashNameArray,
(string)task.flashIndexArray, (bool)task.ChangeYaw, (float)task.HeadYaw, task.singleCopterInfos);
break;
case FlightTaskType.SimpleCircle:
RestoreSimpleCircleTask(task.singleCopterInfos);
break;
case FlightTaskType.ReturnToLand:
// RestoreReturnToLandTask(copters, (int)task.retnumber, (int)task.rtlalt);
break;
}
}
i++;
}
}
public void ImportTasks(string tasksText) public void ImportTasks(string tasksText)
{ {
dynamic tasks = JsonConvert.DeserializeObject(tasksText); dynamic tasks = JsonConvert.DeserializeObject(tasksText);

View File

@ -525,7 +525,6 @@ namespace Plane.FormationCreator.Formation
tasks[i] = await Task.Factory.StartNew(async () => tasks[i] = await Task.Factory.StartNew(async () =>
{ {
var internalInfo = info; var internalInfo = info;
await info.Copter.SetShowLEDAsync(info.FlytoShowLED);
//if (i1 > 0) //if (i1 > 0)
//{ //{
// var prevCopter = infos[i1 - 1].Copter; // var prevCopter = infos[i1 - 1].Copter;
@ -645,6 +644,7 @@ namespace Plane.FormationCreator.Formation
if ((bool)_copterManager.CopterStatus[copterIndex]) if ((bool)_copterManager.CopterStatus[copterIndex])
return; return;
await info.Copter.SetShowLEDAsync(info.FlytoShowLED);
DateTime dtNow = DateTime.Now; DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now; DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime; TimeSpan ts = dtNow - dtLastTime;

View File

@ -70,8 +70,8 @@
<Grid Margin="10,20,10,10"> <Grid Margin="10,20,10,10">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="75*" /> <ColumnDefinition Width="73*" />
<ColumnDefinition Width="25*" MinWidth="360" /> <ColumnDefinition Width="27*" MinWidth="360" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid Margin="0,0,10,0" <Grid Margin="0,0,10,0"

View File

@ -140,6 +140,12 @@ namespace Plane.FormationCreator
} }
if (copterisselect) if (copterisselect)
{ {
// double tlat = _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLat;
// int tmplat = (int)(tlat * 100000);
// tlat = (float)(tmplat * 1.0) / 100000;
// _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLat = tlat;
_flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLat+= e.Key == Key.W ? 0.00001 : -0.00001; _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLat+= e.Key == Key.W ? 0.00001 : -0.00001;
} }
} }
@ -185,6 +191,11 @@ namespace Plane.FormationCreator
} }
if (copterisselect) if (copterisselect)
{ {
// double tlng = _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLng;
// int tmplng = (int)(tlng * 100000);
// tlng = (float)(tmplng * 1.0) / 100000;
// _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLng = tlng;
_flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLng += e.Key == Key.D ? 0.00001 : -0.00001; _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLng += e.Key == Key.D ? 0.00001 : -0.00001;

View File

@ -14,6 +14,7 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Threading; using System.Threading;
using Plane.Communication; using Plane.Communication;
using Plane.Geography;
namespace Plane.FormationCreator.ViewModels namespace Plane.FormationCreator.ViewModels
{ {
@ -52,13 +53,71 @@ namespace Plane.FormationCreator.ViewModels
{ {
var center = _mapManager.Center; var center = _mapManager.Center;
string id; string id;
int colnum = 5; //自动生成列数=4
float coldis = 5;//列相距5米
float rowdis = 5;//行相距5米
int currcol = 0; //当前列号
int currrow = 0; //当前行
Tuple<double, double> colheadLatLng = new Tuple<double, double>(0, 0);
Tuple<double, double> targetLatLng = new Tuple<double, double>(0, 0);
LatLng? colLatLng = new LatLng(center.Lat, center.Lng); ;
for (int i = 0; i < addcount; ++i, ++_virtualCopterId) for (int i = 0; i < addcount; ++i, ++_virtualCopterId)
{ {
id = _virtualCopterId.ToString(); id = _virtualCopterId.ToString();
_lastVirtualCopterLocation =
if (i == 0)
{
_lastVirtualCopterLocation = new LatLng(center.Lat, center.Lng);
colLatLng = _lastVirtualCopterLocation;
}
else
{
if (currcol < colnum)
targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(colLatLng.Value.Lat, colLatLng.Value.Lng , 90, currcol * coldis);
else
{
currrow++;
currcol = 0;
targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(colLatLng.Value.Lat, colLatLng.Value.Lng, 180, rowdis);
colLatLng = new LatLng(targetLatLng.Item1, targetLatLng.Item2);
colheadLatLng = targetLatLng;
}
_lastVirtualCopterLocation= new LatLng(targetLatLng.Item1, targetLatLng.Item2);
}
currcol++;
/* _lastVirtualCopterLocation =
_lastVirtualCopterLocation == null ? _lastVirtualCopterLocation == null ?
new LatLng(center.Lat, center.Lng) : new LatLng(center.Lat, center.Lng) :
new LatLng(_lastVirtualCopterLocation.Value.Lat, _lastVirtualCopterLocation.Value.Lng + 0.0001); new LatLng(_lastVirtualCopterLocation.Value.Lat, _lastVirtualCopterLocation.Value.Lng + 0.0001);
*/
var copter = new FakeCopter(SynchronizationContext.Current); var copter = new FakeCopter(SynchronizationContext.Current);
copter.SetProperties( copter.SetProperties(

View File

@ -149,6 +149,24 @@ namespace Plane.FormationCreator.ViewModels
get { return _Distancevalue; } get { return _Distancevalue; }
set { Set(nameof(Distancevalue), ref _Distancevalue, value); } set { Set(nameof(Distancevalue), ref _Distancevalue, value); }
} }
private int _txtStarindex=0;
public int txtStarindex
{
get { return _txtStarindex; }
set { Set(nameof(txtStarindex), ref _txtStarindex, value); }
}
private int _txtendindex=0;
public int txtendindex
{
get { return _txtendindex; }
set { Set(nameof(txtendindex), ref _txtendindex, value); }
}
private double _FlyToLat; private double _FlyToLat;
public double FlyToLat public double FlyToLat
{ {
@ -212,8 +230,23 @@ namespace Plane.FormationCreator.ViewModels
}; };
if (dialog.ShowDialog() == true) if (dialog.ShowDialog() == true)
{ {
int _startindex = txtStarindex;
int _endindex= txtendindex;
var tasksText = File.ReadAllText(dialog.FileName); var tasksText = File.ReadAllText(dialog.FileName);
if ((txtStarindex == 0) && (txtendindex == 0))
_flightTaskManager.ImportTasks(tasksText); _flightTaskManager.ImportTasks(tasksText);
else
{
_endindex = txtendindex;
if (_startindex == 0)
_startindex = 1;
if (_endindex == 0)
_endindex = _startindex;
_flightTaskManager.ImportTasksindex(tasksText, _startindex, _endindex);
}
} }
})); }));
} }
@ -221,6 +254,7 @@ namespace Plane.FormationCreator.ViewModels
private ICommand _LevelAverageCommand; private ICommand _LevelAverageCommand;
public ICommand LevelAverageCommand public ICommand LevelAverageCommand
{ {
@ -355,7 +389,7 @@ namespace Plane.FormationCreator.ViewModels
if (copterisselect) if (copterisselect)
{ {
_flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLat = minlat + avgl * coptnum; _flightTaskManager.SelectedTask.SingleCopterInfos[i].TargetLat = maxlat - avgl * coptnum;
coptnum++; coptnum++;
} }

View File

@ -57,7 +57,7 @@
<TextBox x:Name="txtVirtualCopterCount" <TextBox x:Name="txtVirtualCopterCount"
Width="35" Width="35"
Margin="0,0,5,0" Margin="0,0,5,0"
Text="3" Text="10"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<Button Content="添加虚拟飞行器" <Button Content="添加虚拟飞行器"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"

View File

@ -22,12 +22,24 @@
</bingMaps:Map.Center> </bingMaps:Map.Center>
<!--<m:OpenStreetMapTileLayer UriFormat="http://tile.openstreetmap.org/{z}/{x}/{y}.png" />--> <!--<m:OpenStreetMapTileLayer UriFormat="http://tile.openstreetmap.org/{z}/{x}/{y}.png" />-->
</bingMaps:Map> </bingMaps:Map>
<!--<Grid HorizontalAlignment="Right" <StackPanel HorizontalAlignment="Right"
VerticalAlignment="Top"> VerticalAlignment="Top"
<Button Content="测距" /> Orientation="Horizontal"
</Grid>--> >
<CheckBox Grid.Row="0" Content="计划航线" Margin="5,5,0,0"
Click="showpanline_Checked"
Foreground="White"
/>
<CheckBox Grid.Row="0" Content="实时航线" Margin="5,5,0,0"
Foreground="White"
Click="showrealtimeline_Checked" />
<ComboBox x:Name="MapSelectionComboBox" <ComboBox x:Name="MapSelectionComboBox"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Foreground="White"
Background="#232323"
Margin="5,0,0,0"
VerticalAlignment="Top" VerticalAlignment="Top"
SelectionChanged="MapSelectionComboBox_SelectionChanged"> SelectionChanged="MapSelectionComboBox_SelectionChanged">
<ComboBoxItem Content="卫星地图" <ComboBoxItem Content="卫星地图"
@ -38,5 +50,13 @@
<ComboBoxItem Content="OpenStreet" <ComboBoxItem Content="OpenStreet"
Tag="OpenStreet" /> Tag="OpenStreet" />
</ComboBox> </ComboBox>
</StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -138,6 +138,15 @@ namespace Plane.FormationCreator.Views
var center = _appConfig.Center; var center = _appConfig.Center;
map.Center = new Location(center.Lat, center.Lng); map.Center = new Location(center.Lat, center.Lng);
map.ZoomLevel = _appConfig.ZoomLevel; map.ZoomLevel = _appConfig.ZoomLevel;
map.ViewChangeOnFrame += (object sender, MapEventArgs e) =>
{
if ( map.Mode.GetType().ToString() == "Microsoft.Maps.MapControl.WPF.AerialMode")
if (map.ZoomLevel >19)
map.ZoomLevel = 19;
};
} }
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>(); private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
@ -268,6 +277,7 @@ namespace Plane.FormationCreator.Views
RemoveTileLayers(); RemoveTileLayers();
break; break;
case "道路": case "道路":
// map.Mode = new GoogleMode();
map.Mode = new RoadMode(); map.Mode = new RoadMode();
RemoveTileLayers(); RemoveTileLayers();
break; break;
@ -275,12 +285,38 @@ namespace Plane.FormationCreator.Views
map.Mode = new MercatorMode(); map.Mode = new MercatorMode();
if (!map.Children.OfType<OpenStreetMapTileLayer>().Any()) if (!map.Children.OfType<OpenStreetMapTileLayer>().Any())
{ {
map.Children.Add(new OpenStreetMapTileLayer { UriFormat = "http://tile.openstreetmap.org/{z}/{x}/{y}.png" }); map.Children.Add(new OpenStreetMapTileLayer
{ UriFormat =
"http://tile.openstreetmap.org/{z}/{x}/{y}.png"
});
} }
break; break;
} }
} }
private void showpanline_Checked(object sender, RoutedEventArgs e)
{
CheckBox chk = (CheckBox)sender;
foreach (var item in _copterDrawings)
{
var copterDrawing = item.Value;
copterDrawing.SetShowroute(chk.IsChecked);
}
}
private void showrealtimeline_Checked(object sender, RoutedEventArgs e)
{
CheckBox chk = (CheckBox)sender;
foreach (var item in _copterDrawings)
{
var copterDrawing = item.Value;
copterDrawing.SetShowtrack (chk.IsChecked);
}
}
private void RemoveTileLayers() private void RemoveTileLayers()
{ {
for (int i = map.Children.Count - 1; i >= 0; i--) for (int i = map.Children.Count - 1; i >= 0; i--)

View File

@ -101,7 +101,9 @@ namespace Plane.FormationCreator.Views
Track.Stroke = _brush; Track.Stroke = _brush;
Track.StrokeThickness = 3.0; Track.StrokeThickness = 3.0;
Track.Locations = new LocationCollection(); Track.Locations = new LocationCollection();
_map.Children.Add(Track);
//实时飞行航线
// _map.Children.Add(Track);
Dot = new Polygon(); Dot = new Polygon();
@ -133,7 +135,12 @@ namespace Plane.FormationCreator.Views
this.Route = new MapPolyline { Locations = new LocationCollection { location } }; this.Route = new MapPolyline { Locations = new LocationCollection { location } };
this.Route.Stroke = _brush; this.Route.Stroke = _brush;
this.Route.StrokeThickness = 2.0; this.Route.StrokeThickness = 2.0;
_map.Children.Add(this.Route); //显示计划航线
// _map.Children.Add(this.Route);
this.LastLocation = location; this.LastLocation = location;
RegisterEventHandlersForDraggingCopter(DotContainer); RegisterEventHandlersForDraggingCopter(DotContainer);
@ -339,6 +346,26 @@ namespace Plane.FormationCreator.Views
} }
} }
public void SetShowroute(bool? showroute)
{
if (showroute ?? false )
_map.Children.Add(this.Route);
else
_map.Children.Remove(this.Route);
}
public void SetShowtrack(bool? showtrack)
{
if (showtrack ?? false)
_map.Children.Add(Track);
else
_map.Children.Remove(Track);
}
public void SetEffect(bool selected) public void SetEffect(bool selected)
{ {
Dot.Effect = selected ? _selectedEffect : null; Dot.Effect = selected ? _selectedEffect : null;

View File

@ -7,7 +7,7 @@
xmlns:vm="clr-namespace:Plane.FormationCreator.ViewModels" xmlns:vm="clr-namespace:Plane.FormationCreator.ViewModels"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation" xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="300" Height="400"> d:DesignWidth="300" Height="420">
<StackPanel Orientation="Vertical" Margin="0,0,0,-28.5"> <StackPanel Orientation="Vertical" Margin="0,0,0,-28.5">
<StackPanel.Resources> <StackPanel.Resources>
@ -16,6 +16,9 @@
Value="Horizontal" /> Value="Horizontal" />
</Style> </Style>
</StackPanel.Resources> </StackPanel.Resources>
<TextBlock Text="任务操作" FontWeight="Bold" />
<StackPanel> <StackPanel>
<Button Content="导出任务" <Button Content="导出任务"
@ -23,18 +26,24 @@
Command="{Binding ExportTasksCommand}" /> Command="{Binding ExportTasksCommand}" />
<Button Content="导入任务" <Button Content="导入任务"
Margin="5,5,0,0" Margin="5,5,0,0"
Command="{Binding ImportTasksCommand}" /> Command="{Binding ImportTasksCommand}"
<TextBlock Text="同时起飞数量:" Margin="5,10,5,0" />
<TextBox x:Name="txttakeoff"
Width="25"
Margin="0,5,5,0"
VerticalContentAlignment="Center"
DataContext="{Binding FlightTaskManager}"
Text="{Binding TakeOffNumAttr, UpdateSourceTrigger=PropertyChanged}"
/> />
<TextBlock Text="导入起始" Margin="5, 10, 5, 0"/>
<TextBox x:Name="txtStarindex"
Width="45"
Margin="0,5,5,0"
VerticalContentAlignment="Center"
Text="{Binding txtStarindex, UpdateSourceTrigger=PropertyChanged}"
/>
<TextBlock Text="结束" Margin="0, 10, 5, 0"/>
<TextBox x:Name="txtendindex"
Width="45"
Margin="0,5,5,0"
VerticalContentAlignment="Center"
Text="{Binding txtendindex, UpdateSourceTrigger=PropertyChanged}"
/>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<Button Content="上边对齐" <Button Content="上边对齐"
@ -69,12 +78,14 @@
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<TextBlock Text="度" Margin="0, 10, 5, 0"/> <TextBlock Text="度" Margin="0, 10, 5, 0"/>
</StackPanel> </StackPanel>
<TextBlock Text="垂直旋转前必须高度相同需要45度表演请转-135度" Margin="0, 10, 5, 0"/>
<StackPanel> <StackPanel>
<Button Content="缩放比例" <Button Content="缩放比例"
Margin="0,5,5,0" Margin="0,5,5,0"
Command="{Binding ScaleCommand}" Command="{Binding ScaleCommand}"
@ -102,6 +113,21 @@
<TextBlock Text="米" Margin="0, 10, 5, 0"/> <TextBlock Text="米" Margin="0, 10, 5, 0"/>
</StackPanel> </StackPanel>
<Separator />
<TextBlock Text="任务属性" Margin="0, 0, 5, 0" FontWeight="Bold" />
<StackPanel>
<TextBlock Text="起飞数量:" Margin="5,10,5,0" />
<TextBox x:Name="txttakeoff"
Width="25"
Margin="0,5,5,0"
VerticalContentAlignment="Center"
DataContext="{Binding FlightTaskManager}"
Text="{Binding TakeOffNumAttr, UpdateSourceTrigger=PropertyChanged}"
/>
</StackPanel>
<TabControl Margin="0,5" <TabControl Margin="0,5"
Grid.IsSharedSizeScope="True" Grid.IsSharedSizeScope="True"

View File

@ -46,6 +46,7 @@
</Button.Template> </Button.Template>
</Button> </Button>
<Button HorizontalAlignment="Left" <Button HorizontalAlignment="Left"
Background="#232323"
Visibility="{Binding FlightTaskManager.IsPaused, Converter={StaticResource IsPausedToPauseButtonVisibilityConverter}}" Visibility="{Binding FlightTaskManager.IsPaused, Converter={StaticResource IsPausedToPauseButtonVisibilityConverter}}"
Command="{Binding PauseTasksCommand}"> Command="{Binding PauseTasksCommand}">
<Button.Template> <Button.Template>
@ -114,10 +115,13 @@
Orientation="Horizontal" Orientation="Horizontal"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
<Button Content="添加任务" <Button Content="添加任务"
Background="#232323"
Command="{Binding AddTaskCommand}" /> Command="{Binding AddTaskCommand}" />
<Button Content="清除任务" <Button Content="清除任务"
Background="#232323"
Command="{Binding ClearTasksCommand}" /> Command="{Binding ClearTasksCommand}" />
<Button Content="重置任务" <Button Content="重置任务"
Background="#232323"
Command="{Binding ResetTasksCommand}" /> Command="{Binding ResetTasksCommand}" />
<!--<Button Content="保存" /> <!--<Button Content="保存" />
<Button Content="取消" />--> <Button Content="取消" />-->