2024-09-25 V1.0.0.0

master
tayttt 2 months ago
parent e63d5a0095
commit 2541f3f8cc

@ -1,6 +1,11 @@
using System.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Configuration;
using System.Data;
using System.Windows;
using Txgy.DataStat.IServices;
using Txgy.DataStat.Services;
using Txgy.DataStat.ViewModels;
using Txgy.DataStat.Views;
namespace Txgy.DataStat
{
@ -9,6 +14,24 @@ namespace Txgy.DataStat
/// </summary>
public partial class App : Application
{
public new static App Current => (App)Application.Current;
public IServiceProvider Services { get; }
public App()
{
var services = new ServiceCollection();
// Register services
services.AddSingleton<IDataService, DataService>();
// Register view models
services.AddTransient<UserControlAViewModel>();
services.AddTransient<UserControlBViewModel>();
services.AddTransient<LoadDataFromFileViewModel>();
Services = services.BuildServiceProvider();
}
}
}

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.IServices
{
public interface IDataService
{
ObservableCollection<City> Cities { get; }
void LoadCitiesFromFile(string filePath);
void SaveCitiesToFile(string filePath);
}
}

@ -0,0 +1,28 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Txgy.DataStat.Models
{
public class City : ObservableObject
{
private string _name;
private string _area;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public string Area
{
get => _area;
set => SetProperty(ref _area, value);
}
}
}

@ -0,0 +1,111 @@
using LiveChartsCore.Drawing;
using LiveChartsCore.Kernel.Events;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.Measure;
using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
using LiveChartsCore.SkiaSharpView.Drawing;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.VisualElements;
using LiveChartsCore.VisualElements;
using LiveChartsCore;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LiveChartsCore.SkiaSharpView;
using System.Windows;
namespace Txgy.DataStat.Models
{
public class CustomLegend : IChartLegend<SkiaSharpDrawingContext>
{
private static readonly int s_zIndex = 10050;
private readonly StackPanel<RoundedRectangleGeometry, SkiaSharpDrawingContext> _stackPanel = new();
private readonly SolidColorPaint _fontPaint = new(new SKColor(30, 20, 30))
{
SKTypeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold),
ZIndex = s_zIndex + 1
};
public void Draw(Chart<SkiaSharpDrawingContext> chart)
{
var legendPosition = chart.GetLegendPosition();
_stackPanel.X = legendPosition.X;
_stackPanel.Y = legendPosition.Y;
chart.AddVisual(_stackPanel);
if (chart.LegendPosition == LegendPosition.Hidden) chart.RemoveVisual(_stackPanel);
}
public LvcSize Measure(Chart<SkiaSharpDrawingContext> chart)
{
_stackPanel.Orientation = ContainerOrientation.Horizontal;
_stackPanel.HorizontalAlignment = Align.End;
_stackPanel.MaxWidth = double.MaxValue;
_stackPanel.MaxHeight = chart.ControlSize.Height;
// clear the previous elements.
foreach (var visual in _stackPanel.Children.ToArray())
{
_ = _stackPanel.Children.Remove(visual);
chart.RemoveVisual(visual);
}
var theme = LiveCharts.DefaultSettings.GetTheme<SkiaSharpDrawingContext>();
foreach (var series in chart.Series.Where(x => x.IsVisibleAtLegend))
{
var color = new SolidColorPaint(theme.GetSeriesColor(series).AsSKColor()) { ZIndex=s_zIndex+1};
var panel = new StackPanel<RectangleGeometry, SkiaSharpDrawingContext>
{
Padding = new Padding(12, 6),
VerticalAlignment = Align.Middle,
HorizontalAlignment = Align.End,
Children =
{
new SVGVisual
{
Path = SKPath.ParseSvgPathData(SVGPoints.Square),
Width = 25,
Height = 25,
ClippingMode = ClipMode.None, // required on legends // mark
Fill=color
//Fill = new SolidColorPaint(theme.GetSeriesColor(series).AsSKColor())
//{
// ZIndex = s_zIndex + 1
//}
},
new LabelVisual
{
Text = series.Name ?? string.Empty,
Paint = _fontPaint,
TextSize = 15,
ClippingMode = ClipMode.None, // required on legends // mark
Padding = new Padding(8, 0, 0, 0),
VerticalAlignment = Align.Start,
HorizontalAlignment = Align.Start
}
}
};
panel.PointerDown += GetPointerDownHandler(series);
_stackPanel.Children.Add(panel);
}
return _stackPanel.Measure(chart);
}
private static VisualElementHandler<SkiaSharpDrawingContext> GetPointerDownHandler(
IChartSeries<SkiaSharpDrawingContext> series)
{
return (visual, args) =>
{
series.IsVisible = !series.IsVisible;
};
}
}
}

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Txgy.DataStat.Models
{
public class EventTimeStatsResult
{
public double time { get; set; }
public double total { get; set; }
}
}

@ -0,0 +1,22 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Txgy.DataStat.Models
{
public class FracCurveDataSchme : ObservableObject
{
public int FirstDataRow { get; set; } = 3;
public int TimeCol { get; set; } = 1;
public int SurPreCol { get; set; } = 2;
public int CasingPreCol { get; set; } = 3;
public int SlurryRateCol { get; set; } = 7;
public string Separator { get; set; } = "tab";
public string DPoint { get; set; } = "dot";
}
}

@ -0,0 +1,97 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Txgy.DataStat.Models
{
public partial class FracDataReader : ObservableObject
{
[ObservableProperty]
bool absoluteTime = false;
[ObservableProperty]
string filePath = "";
public int FirstDataRow { get; set; } = 3;
public int TimeCol { get; set; } = 1;
public int SurPreCol { get; set; } = 2;
public int CasingPreCol { get; set; } = 3;
public int SlurryRateCol { get; set; } = 7;
public string Separator { get; set; } = "tab";
public string DPoint { get; set; } = "dot";
public double skipValue = 1;
public List<FracDataRow> ReadData(double timeSpan)
{
var dataRows = new List<FracDataRow>();
//try
{
if (FilePath != "")
{
string allTexts = File.ReadAllText(FilePath);
string[] rows = allTexts.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (rows.Length > 0)
{
var sep = GetDelimiter(Separator);
DateTime startTime = new DateTime();
if(AbsoluteTime)
startTime = DateTime.Parse(rows[FirstDataRow - 1].Split(sep)[TimeCol - 1]);
for (int i = FirstDataRow - 1; i < rows.Length; i++)
{
var cols = rows[i].Trim().Split(sep, StringSplitOptions.RemoveEmptyEntries);
//double skip = double.Parse(cols[TimeCol - 1].Trim()) / skipValue;
//double skip = Math.Floor(double.Parse(cols[TimeCol - 1].Trim()));
//Debug.WriteLine(skip);
if (cols.Length > 0)
{
//double skip = double.Parse(cols[TimeCol - 1].Trim());
//if (Math.Abs(skip % skipValue)<1e-10)
{
var fracRow = new FracDataRow
{
time = AbsoluteTime? (DateTime.Parse(cols[TimeCol - 1])-startTime).TotalSeconds/ timeSpan : double.Parse(cols[TimeCol - 1].Trim()),
surPre = double.Parse(cols[SurPreCol - 1].Trim()),
casingPre = double.Parse(cols[CasingPreCol - 1].Trim()),
slurryRate = double.Parse(cols[SlurryRateCol - 1].Trim()),
};
dataRows.Add(fracRow);
}
}
}
}
}
}
//catch (Exception ex)
//{
// Console.WriteLine($"Error reading file: {ex.Message}");
//}
return dataRows;
}
char GetDelimiter(string delimiterName)
{
return delimiterName.ToLower() switch
{
"tab" => '\t',
"comma" => ',',
"semicolon" => ';',
"dot" => '.',
_ => throw new ArgumentException("Unsupported delimiter")
};
}
}
public class FracDataRow
{
public double time { get; set; }
public double surPre { get; set; }
public double casingPre { get; set; }
public double slurryRate { get; set; }
}
}

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Txgy.DataStat.Models
{
public class PosData
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public double Time { get; set; }
}
}

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Txgy.DataStat.IServices;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.Services
{
public class DataService : IDataService
{
public ObservableCollection<City> Cities { get; }
public DataService()
{
Cities = new ObservableCollection<City>
{
new City { Name = "City1", Area = "Area1" },
new City { Name = "City2", Area = "Area2" },
new City { Name = "City3", Area = "Area3" },
};
}
public void LoadCitiesFromFile(string filePath)
{
// 假设文件是CSV格式包含Name和Area两列
var lines = System.IO.File.ReadAllLines(filePath);
Cities.Clear();
foreach (var line in lines)
{
var parts = line.Split(',');
if (parts.Length == 2)
{
Cities.Add(new City { Name = parts[0], Area = parts[1] });
}
}
}
public void SaveCitiesToFile(string filePath)
{
var lines = Cities.Select(city => $"{city.Name},{city.Area}");
System.IO.File.WriteAllLines(filePath, lines);
}
}
}

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.Services
{
public static class PosDataService
{
public static List<PosData> ReadPosDataFromFile(string filePath)
{
List<PosData> datas = new List<PosData>();
string str = File.ReadAllText(filePath);
string[] rows = str.Trim().Split('\n');
for (int i = 1; i < rows.Length; i++)
{
var cols = rows[i].Trim().Split("\t", StringSplitOptions.RemoveEmptyEntries);
var pos = new PosData
{
X = double.Parse(cols[0]),
Y = double.Parse(cols[1]),
Z = double.Parse(cols[2]),
Time = double.Parse(cols[3]),
};
datas.Add(pos);
}
return datas;
}
public static List<EventTimeStatsResult> StatsTimeFreq(List<PosData> datas, int StatsStep)
{
List<EventTimeStatsResult> results = new List<EventTimeStatsResult>();
int statsCount = (int)((float)(datas[datas.Count - 1].Time) / StatsStep) + 1;
int[] slArr = new int[statsCount];
float[] sjArr = new float[statsCount];
int curIndex = 0;
for (int i = 0; i < slArr.Length; i++)
{
int curSj = (i + 1) * (int)StatsStep;
sjArr[i] = (float)curSj / StatsStep;
while (datas[curIndex].Time < curSj)
{
slArr[i]++;
curIndex++;
if (curIndex >= datas.Count)
{
break;
}
}
results.Add(new EventTimeStatsResult { time = sjArr[i], total = slArr[i] });
}
return results;
}
}
}

@ -13,13 +13,13 @@
<PackageReference Include="HandyControls" Version="3.5.3" />
<PackageReference Include="LiveChartsCore.SkiaSharpView" Version="2.0.0-rc3.3" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc3.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="SkiaSharp" Version="2.88.8" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.8" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
<Folder Include="ValueConverters\" />
</ItemGroup>

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HandyControl.Controls;
using Txgy.DataStat.IServices;
using Txgy.DataStat.Models;
using Txgy.DataStat.Services;
using Txgy.DataStat.Views;
namespace Txgy.DataStat.ViewModels
{
public partial class LoadDataFromFileViewModel : ObservableObject
{
private readonly IDataService _dataService;
public ObservableCollection<City> Cities => _dataService.Cities;
public ObservableCollection<City> C2 => new ObservableCollection<City> { new City { Name="河北",Area="100"} };
[ObservableProperty]
string selectedFilePath = "";
[ObservableProperty]
string saveFilePath = "";
[RelayCommand]
void ChooseFile()
{
var openFileDialog = new Microsoft.Win32.OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
{
SelectedFilePath = openFileDialog.FileName;
}
}
[RelayCommand]
void LoadCities()
{
if (!string.IsNullOrEmpty(SelectedFilePath))
_dataService.LoadCitiesFromFile(SelectedFilePath);
}
[RelayCommand]
void ChooseSaveFile()
{
var saveFileDialog = new Microsoft.Win32.SaveFileDialog();
saveFileDialog.Filter = "City files (*.txt)|*.txt";
if (saveFileDialog.ShowDialog() == true)
{
SaveFilePath = saveFileDialog.FileName;
}
}
[RelayCommand]
void SaveCities()
{
if (!string.IsNullOrEmpty(SaveFilePath))
{
_dataService.SaveCitiesToFile(SaveFilePath);
Dialog.Show(new CustomDialog());
}
}
public LoadDataFromFileViewModel(IDataService dataService)
{
_dataService = dataService;
}
}
}

@ -1,9 +1,26 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.TextFormatting;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LiveChartsCore;
using LiveChartsCore.Drawing;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.VisualElements;
using Microsoft.Win32;
using SkiaSharp;
using Txgy.DataStat.Models;
using Txgy.DataStat.Services;
using static System.Net.Mime.MediaTypeNames;
namespace Txgy.DataStat.ViewModels
{
@ -11,5 +28,368 @@ namespace Txgy.DataStat.ViewModels
{
[ObservableProperty]
string message = "Hello tay";
[ObservableProperty]
FracDataReader fracReader = new FracDataReader();
[ObservableProperty]
double startTime = 0;
[ObservableProperty]
double endTime = 55;
[ObservableProperty]
string posFilePath = "";
[ObservableProperty]
string wellName = "276";
//[ObservableProperty]
//double yAxis1Max = 500;
private double yAxisSurPreMax = 500;
public double YAxisSurPreMax
{
get { return yAxisSurPreMax; }
set
{
SetProperty(ref yAxisSurPreMax, value);
if (YAxes[1] != null)
YAxes[1].MaxLimit = yAxisSurPreMax;
}
}
private double yAxisEventsMax = 10;
public double YAxisEventsMax
{
get { return yAxisEventsMax; }
set
{
SetProperty(ref yAxisEventsMax, value);
if (YAxes[0] != null)
YAxes[0].MaxLimit = yAxisEventsMax;
}
}
private string timeUnitSelected = "分";
public string TimeUnitSelected
{
get { return timeUnitSelected; }
set
{
SetProperty(ref timeUnitSelected, value);
TimeUnit = timeUnitSelected == "分" ? 60 : 1;
}
}
int TimeUnit = 60;
public List<string> TimeUnits { get; } = new List<string> { "分", "秒" };
List<FracDataRow> ListFracData = new List<FracDataRow>();
List<EventTimeStatsResult> eventFreqs = new List<EventTimeStatsResult>();
private static readonly SKColor s_blue = new(25, 118, 210);
private static readonly SKColor s_red = new(229, 57, 53);
private static readonly SKColor s_yellow = new(198, 167, 0);
private static readonly SKColor s_SlurryRate = new(25, 118, 210);
private static readonly SKColor s_SurPre = new(229, 57, 53);
private static readonly SKColor s_CasingPre = new(198, 167, 0);
private static readonly SKColor s_Events = new(68, 84, 106);
//public ObservableCollection<ISeries> Series { get; set; }
[ObservableProperty]
LabelVisual title = new LabelVisual
{
Text = "276",
TextSize = 25,
Padding = new LiveChartsCore.Drawing.Padding(15),
Paint = new SolidColorPaint(SKColors.DarkSlateGray),
};
[ObservableProperty]
private ISeries[] mySeries;
public ISeries[] Series { get; set; } =
{
new ColumnSeries<int>
{
Name = "Event Frequency",
Values = new[] { 0 },
Stroke = null,
MaxBarWidth = 10,
// but you can customize the gradient
Fill = new LinearGradientPaint(
// the gradient will use the following colors array
new[] { s_Events, s_Events },
// we must go from the point:
// (x0, y0) where x0 could be read as "the middle of the x axis" (0.5) and y0 as "the start of the y axis" (0)
new SKPoint(0.5f, 0),
// to the point:
// (x1, y1) where x1 could be read as "the middle of the x axis" (0.5) and y0 as "the end of the y axis" (1)
new SKPoint(0.5f, 1)
),
ScalesYAt = 0,
},
new LineSeries<double>
{
LineSmoothness = 1,
Name = "Surface Pressure",
Values = new double[] { 300, 400, 500, 400, 300 },
Stroke = new SolidColorPaint(s_SurPre, 2),
GeometrySize = 0,
GeometryStroke = null,
Fill = null,
ScalesYAt = 1,
},
new LineSeries<double>
{
Name = "Casing Pressure",
Values = new double[] { 50, 75, 100, 75, 50 },
Stroke = new SolidColorPaint(s_CasingPre, 2),
GeometrySize = 0,
GeometryStroke = null,
Fill = null,
ScalesYAt = 2,
},
new LineSeries<double>
{
Name = "Slurry Rate",
Values = new double[] { 5, 6, 3, 2, 1 },
Stroke = new SolidColorPaint(s_SlurryRate, 2),
GeometrySize = 0,
GeometryStroke = null,
Fill = null,
ScalesYAt = 3,
},
};
public ICartesianAxis[] YAxes { get; set; } =
{
new Axis // the "hundreds" series will be scaled on this axis
{
Name = "Events",
NameTextSize = 14,
NamePaint = new SolidColorPaint
{
Color = s_Events,
FontFamily = "微软雅黑",
SKFontStyle = new SKFontStyle(
SKFontStyleWeight.ExtraBold,
SKFontStyleWidth.Normal,
SKFontStyleSlant.Upright
),
},
NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
Padding = new LiveChartsCore.Drawing.Padding(20, 0, 0, 0),
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_Events),
TicksPaint = new SolidColorPaint(s_Events),
SubticksPaint = new SolidColorPaint(s_Events),
DrawTicksPath = true,
ShowSeparatorLines = false,
Position = LiveChartsCore.Measure.AxisPosition.End,
MinStep = 1,
MinLimit = 0,
MaxLimit = 10,
},
new Axis // the "units" and "tens" series will be scaled on this axis
{
Name = "Surface Pressure",
NameTextSize = 14,
NamePaint = new SolidColorPaint
{
Color = s_SurPre,
FontFamily = "微软雅黑",
SKFontStyle = new SKFontStyle(
SKFontStyleWeight.ExtraBold,
SKFontStyleWidth.Normal,
SKFontStyleSlant.Upright
),
},
NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
Padding = new LiveChartsCore.Drawing.Padding(0, 0, 20, 0),
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_SurPre),
TicksPaint = new SolidColorPaint(s_SurPre),
SubticksPaint = new SolidColorPaint(s_SurPre),
DrawTicksPath = true,
MinLimit = 0,
MaxLimit = 600,
},
new Axis // the "hundreds" series will be scaled on this axis
{
Name = "Casing Pressure",
NameTextSize = 14,
NamePaint = new SolidColorPaint
{
Color = s_CasingPre,
FontFamily = "微软雅黑",
SKFontStyle = new SKFontStyle(
SKFontStyleWeight.ExtraBold,
SKFontStyleWidth.Normal,
SKFontStyleSlant.Upright
),
},
NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
Padding = new LiveChartsCore.Drawing.Padding(20, 0, 0, 0),
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_CasingPre),
TicksPaint = new SolidColorPaint(s_CasingPre),
SubticksPaint = new SolidColorPaint(s_CasingPre),
DrawTicksPath = true,
ShowSeparatorLines = false,
Position = LiveChartsCore.Measure.AxisPosition.End,
MinLimit=0,
MaxLimit = 300,
IsVisible = false,
},
new Axis // the "thousands" series will be scaled on this axis
{
Name = "Slurry Rate",
NameTextSize = 14,
NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
Padding = new LiveChartsCore.Drawing.Padding(20, 0, 0, 0),
NamePaint = new SolidColorPaint
{
Color = s_SlurryRate,
FontFamily = "微软雅黑",
SKFontStyle = new SKFontStyle(
SKFontStyleWeight.ExtraBold,
SKFontStyleWidth.Normal,
SKFontStyleSlant.Upright
),
},
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_SlurryRate),
TicksPaint = new SolidColorPaint(s_SlurryRate),
SubticksPaint = new SolidColorPaint(s_SlurryRate),
DrawTicksPath = true,
ShowSeparatorLines = false,
Position = LiveChartsCore.Measure.AxisPosition.End,
MinLimit=0,
MaxLimit = 10,
IsVisible = false,
},
};
public Axis[] XAxis { get; set; } = new Axis[] { new Axis { MinLimit = 0,MinStep=60,Name="Time(min)" } };
public SolidColorPaint LegendTextPaint { get; set; } =
new SolidColorPaint
{
Color = new SKColor(50, 50, 50),
SKTypeface = SKTypeface.FromFamilyName("Courier New"),
};
public SolidColorPaint LedgendBackgroundPaint { get; set; } =
new SolidColorPaint(new SKColor(240, 240, 240));
[RelayCommand]
void InputFracData()
{
UpdateWellName();
ListFracData = FracReader.ReadData((double)TimeUnit);
var serie = Series[1];
serie.Values = ListFracData
.Where(data => data.time > StartTime && data.time < EndTime * TimeUnit)
.Select(data => data.surPre)
.ToArray();
var serie1 = Series[2];
serie1.Values = ListFracData
.Where(data => data.time > StartTime && data.time < EndTime * TimeUnit)
.Select(data => data.casingPre)
.ToArray();
var serie2 = Series[3];
serie2.Values = ListFracData
.Where(data => data.time > StartTime && data.time < EndTime * TimeUnit)
.Select(data => data.slurryRate)
.ToArray();
SetXAxis();
}
void SetXAxis()
{
var axis = XAxis[0];
var lbls = ListFracData
.Select(data => Math.Round(data.time, 0).ToString())
.ToArray();
axis.Labels = lbls;
axis.MinStep = 800;
//axis.MaxLimit = 55 * 60;
//axis.MinLimit = StartTime;
axis.MaxLimit = EndTime * TimeUnit;
}
[RelayCommand]
void SelectFracFile()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "压裂曲线数据|*.txt";
if (ofd.ShowDialog() == true)
{
FracReader.FilePath = ofd.FileName;
Message=Path.GetFileNameWithoutExtension(ofd.FileName);
}
}
[RelayCommand]
void SelectPosFile()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "微地震数据|*.pos";
if (ofd.ShowDialog() == true)
{
PosFilePath = ofd.FileName;
}
}
[RelayCommand]
void CountEventFrequency()
{
var posDatas = PosDataService.ReadPosDataFromFile(PosFilePath);
eventFreqs = PosDataService.StatsTimeFreq(posDatas, 60);
UpdateWellName();
UpdateFreqSerier();
}
void UpdateFreqSerier()
{
var serieEvents = Series[0];
List<int> freqDatas = new List<int>();
for (int i = 1; i < ListFracData.Count; i++)
{
var time = ListFracData[i].time;
if (Math.Abs(time % 1) < 1e-10 && time > 0)
{
var value = eventFreqs
.Where(t => t.time == time)
.Select(t => t.total)
.ToList();
if (value != null && value.Count > 0)
freqDatas.Add((int)(value[0]));
}
else
{
freqDatas.Add(0);
}
}
serieEvents.Values = freqDatas.ToArray();
}
void UpdateWellName()
{
Title.Text = "KKM-" + WellName + " Fracturing Trends vs Microseismic Frequency";
}
//public void Create
public MainWindowViewModel()
{
//FracDataFormat.FirstDataRow = 100;
//Title2 = new LabelVisual
//{
// Text = "KKM-" + WellName + " Fracturing Trends vs.Microseismic Frequency",
// TextSize = 25,
// Padding = new LiveChartsCore.Drawing.Padding(15),
// Paint = new SolidColorPaint(SKColors.DarkSlateGray),
//};
}
}
}

@ -0,0 +1,16 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.ViewModels
{
public partial class TestItemsSourceViewModel:ObservableObject
{
}
}

@ -0,0 +1,25 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Txgy.DataStat.IServices;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.ViewModels
{
public class UserControlAViewModel : ObservableObject
{
public ObservableCollection<City> Cities => _dataService.Cities;
private readonly IDataService _dataService;
public UserControlAViewModel(IDataService dataService)
{
_dataService = dataService;
// DataList = _dataService.DataList;
}
}
}

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Txgy.DataStat.IServices;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.ViewModels
{
public class UserControlBViewModel
{
public ObservableCollection<City> Cities => _dataService.Cities;
private readonly IDataService _dataService;
public UserControlBViewModel(IDataService dataService)
{
_dataService = dataService;
// DataList = _dataService.DataList;
}
}
}

@ -0,0 +1,42 @@
<UserControl x:Class="Txgy.DataStat.Views.CustomDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Txgy.DataStat.Views"
xmlns:hc="https://handyorg.github.io/handycontrol"
mc:Ignorable="d"
Height="300" Width="500" Background="White"
d:DesignHeight="300"
d:DesignWidth="500">
<!--<Grid>-->
<!--<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="自定义对话框"
Background="BlueViolet"
Foreground="White"
FontSize="20" />
<Button Content="×" FontSize="14"
Command="hc:ControlCommands.Close" />
</StackPanel>
<TextBlock Text="完成啦!"
Grid.Row="1"
VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="60"/>-->
<hc:SimplePanel>
<TextBlock Text="完成啦" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="60"/>
<Button Width="22"
Height="22"
Command="hc:ControlCommands.Close"
Padding="0"
Content="X"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="0,4,4,0" />
</hc:SimplePanel>
<!--</Grid>-->
</UserControl>

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Txgy.DataStat.Views
{
/// <summary>
/// CustomDialog.xaml 的交互逻辑
/// </summary>
public partial class CustomDialog : UserControl
{
public CustomDialog()
{
InitializeComponent();
}
}
}

@ -0,0 +1,50 @@
<UserControl x:Class="Txgy.DataStat.Views.LoadDataFromFileView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Txgy.DataStat.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel>
<WrapPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="导入文件"
FontSize="24"
Margin="5" />
<TextBox Text="{Binding SelectedFilePath}"
Width="450"
Margin="5" />
<Button Content="..."
Width="80"
Margin="5"
Command="{Binding ChooseFileCommand}" />
<Button Content="导入"
Width="80"
Margin="5"
Command="{Binding LoadCitiesCommand}" />
</WrapPanel>
<WrapPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="导出文件"
FontSize="24"
Margin="5" />
<TextBox Text="{Binding SaveFilePath}"
Width="450"
Margin="5" />
<Button Content="..."
Width="80"
Margin="5"
Command="{Binding ChooseSaveFileCommand}" />
<Button Content="导出"
Width="80"
Margin="5"
Command="{Binding SaveCitiesCommand}" />
</WrapPanel>
<WrapPanel >
<local:TestItemsSource ItemsSource="{Binding Cities}" />
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Txgy.DataStat.ViewModels;
namespace Txgy.DataStat.Views
{
/// <summary>
/// LoadDataFromFileView.xaml 的交互逻辑
/// </summary>
public partial class LoadDataFromFileView : UserControl
{
public LoadDataFromFileView()
{
InitializeComponent();
DataContext = App.Current.Services.GetService<LoadDataFromFileViewModel>();
}
}
}

@ -1,16 +1,299 @@
<Window x:Class="Txgy.DataStat.Views.MainWindow"
<Window xmlns:hc="https://handyorg.github.io/handycontrol"
x:Class="Txgy.DataStat.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Txgy.DataStat.ViewModels"
xmlns:local="clr-namespace:Txgy.DataStat"
xmlns:vms="clr-namespace:Txgy.DataStat.Models"
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
xmlns:local="clr-namespace:Txgy.DataStat.Views"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
Title="MainWindow"
Height="800"
Width="800"
WindowState="Maximized">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Message}" FontSize="50"/>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Margin="20,0,0,0" VerticalAlignment="Center" Background="AliceBlue">
<TextBlock Text="{Binding Message}"
FontSize="24" />
<GroupBox Header="FracData">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="数据行"
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.FirstDataRow}"
d:Text="3"
Grid.Row="0"
Grid.Column="1" />
<TextBlock Text="分隔符"
Grid.Row="0"
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.Separator}"
d:Text="TAB"
Grid.Row="0"
Grid.Column="3" />
<TextBlock Text="时间列"
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.TimeCol}"
d:Text="1"
Grid.Row="1"
Grid.Column="1" />
<TextBlock Text="井口压力列"
Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.SurPreCol}"
d:Text="2"
Grid.Row="1"
Grid.Column="3" />
<TextBlock Text="套管压力列"
Grid.Row="2"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.CasingPreCol}"
d:Text="2"
Grid.Row="2"
Grid.Column="1" />
<TextBlock Text="流量"
Grid.Row="2"
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.SlurryRateCol}"
d:Text="7"
Grid.Row="2"
Grid.Column="3" />
<TextBlock Text="小数点"
Grid.Row="3"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.DPoint}"
d:Text="."
Grid.Row="3"
Grid.Column="1" />
<CheckBox Content="绝对时刻"
IsChecked="{Binding FracReader.AbsoluteTime}"
Grid.Row="3"
Grid.Column="2" />
<Button Content="导入"
Command="{Binding InputFracDataCommand}"
Grid.Column="3"
Grid.Row="3" />
<TextBlock Text="文件"
Grid.Row="4"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding FracReader.FilePath}"
d:Text="c:\\1.txt"
Grid.Row="4"
Grid.Column="1"
Grid.ColumnSpan="2" />
<Button Content="..."
Command="{Binding SelectFracFileCommand}"
Grid.Column="3"
Grid.Row="4" />
</Grid>
</GroupBox>
<GroupBox Header="pos" Margin="0,10,0,0">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="PosFile"
VerticalAlignment="Center" />
<TextBox Width="200"
Text="{Binding PosFilePath}" />
<Button Content="..."
Command="{Binding SelectPosFileCommand}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="统计"
Width="80"
Command="{Binding CountEventFrequencyCommand}" />
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="DrawConfig"
Margin="0,10,0,0">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="开始时间"
VerticalAlignment="Center"
Width="50" />
<TextBox Width="80"
Text="{Binding StartTime}" />
<TextBlock Text="统计间隔"
VerticalAlignment="Center"
Width="50"
Margin="10,0,0,0" />
<ComboBox Margin="5,0,0,0"
ItemsSource="{Binding Path=TimeUnits}"
SelectedItem="{Binding TimeUnitSelected}"
Width="80" />
<!--<TextBlock Text="秒"
VerticalAlignment="Center"
Width="50" />-->
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,5,0,0">
<TextBlock Text="结束时间"
VerticalAlignment="Center"
Width="50" />
<TextBox Width="80"
Text="{Binding EndTime}" />
<TextBlock Text="井号"
Margin="10,0,0,0"
VerticalAlignment="Center"
Width="50" />
<TextBox Margin="5,0,0,0"
Text="{Binding WellName}"
Width="80" />
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="YAxis"
Margin="0,10,0,0">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="EventFreq"
Margin="5,0,5,0"
Width="60"
VerticalAlignment="Center" />
<CheckBox Content="Visible"
IsChecked="{Binding YAxes[0].IsVisible}" />
<TextBlock Text="最大值"
Margin="10,0,10,0"
VerticalAlignment="Center" />
<hc:NumericUpDown Minimum="3"
Maximum="20"
Increment="1"
Width="80"
Value="{Binding YAxes[0].MaxLimit}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,5,0,0">
<TextBlock Text="Surface"
Margin="5,0,5,0"
Width="60"
VerticalAlignment="Center" />
<CheckBox Content="Visible"
IsChecked="{Binding YAxes[1].IsVisible}" />
<TextBlock Text="最大值"
Margin="10,0,10,0"
VerticalAlignment="Center" />
<hc:NumericUpDown Minimum="0"
Maximum="2000"
Increment="50"
Width="80"
Value="{Binding YAxes[1].MaxLimit}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,5,0,0">
<TextBlock Text="Casing"
Margin="5,0,5,0"
Width="60"
VerticalAlignment="Center" />
<CheckBox Content="Visible"
IsChecked="{Binding YAxes[2].IsVisible}" />
<TextBlock Text="最大值"
Margin="10,0,10,0"
VerticalAlignment="Center" />
<hc:NumericUpDown Minimum="0"
Maximum="2000"
Increment="50"
Width="80"
Value="{Binding YAxes[2].MaxLimit}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,5,0,0">
<TextBlock Text="Slurry"
Margin="5,0,5,0"
Width="60"
VerticalAlignment="Center" />
<CheckBox Content="Visible"
IsChecked="{Binding YAxes[3].IsVisible}" />
<TextBlock Text="最大值"
Margin="10,0,10,0"
VerticalAlignment="Center" />
<hc:NumericUpDown Minimum="1"
Maximum="20"
Increment="1"
Width="80"
Value="{Binding YAxes[3].MaxLimit}" />
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock x:Name="tbChartTitle"
Text="{Binding WellName,StringFormat=KKM-{0} Fracturing Trends vs Microseismic Frequency}"
HorizontalAlignment="Center"
FontSize="28"
FontFamily="微软雅黑"
FontWeight="Bold" />
<lvc:CartesianChart Series="{Binding Series}"
YAxes="{Binding YAxes}"
LegendPosition="Top"
LegendBackgroundPaint="{Binding LedgendBackgroundPaint}"
LegendTextPaint="{Binding LegendTextPaint}"
LegendTextSize="16"
XAxes="{Binding XAxis}"
TooltipPosition="Hidden"
ZoomMode="X"
Grid.Row="1">
<!--<lvc:CartesianChart.Legend>
<vms:CustomLegend/>
</lvc:CartesianChart.Legend>-->
</lvc:CartesianChart>
</Grid>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!--<local:UserControlA />
<local:UserControlB Grid.Row="1"/>
<local:LoadDataFromFileView Grid.Row="2" />-->
</Grid>
</Grid>
</Window>

@ -0,0 +1,23 @@
<UserControl x:Class="Txgy.DataStat.Views.TestItemsSource"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Txgy.DataStat.Views"
mc:Ignorable="d"
d:DesignHeight="150"
d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="通过设置依赖属性ItemsSource为用户控件的DataGrid的ItemsSource绑定数据示例" FontSize="20"/>
<StackPanel Orientation="Horizontal">
<DataGrid ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TestItemsSource}}}"
AutoGenerateColumns="True" />
<DataGrid ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
AutoGenerateColumns="True"
Height="200" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Txgy.DataStat.Models;
namespace Txgy.DataStat.Views
{
/// <summary>
/// TestItemsSource.xaml 的交互逻辑
/// </summary>
public partial class TestItemsSource : UserControl
{
public TestItemsSource()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<City>), typeof(TestItemsSource), new PropertyMetadata(null));
public ObservableCollection<City> ItemsSource
{
get { return (ObservableCollection<City>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
}
}

@ -0,0 +1,16 @@
<UserControl x:Class="Txgy.DataStat.Views.UserControlA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Txgy.DataStat.Views"
xmlns:vm="clr-namespace:Txgy.DataStat.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<DataGrid ItemsSource="{Binding Cities}">
</DataGrid>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Txgy.DataStat.ViewModels;
namespace Txgy.DataStat.Views
{
/// <summary>
/// UserControlA.xaml 的交互逻辑
/// </summary>
public partial class UserControlA : UserControl
{
public UserControlA()
{
InitializeComponent();
DataContext = App.Current.Services.GetService<UserControlAViewModel>();
}
}
}

@ -0,0 +1,14 @@
<UserControl x:Class="Txgy.DataStat.Views.UserControlB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Txgy.DataStat.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<DataGrid ItemsSource="{Binding Cities}">
</DataGrid>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Txgy.DataStat.ViewModels;
namespace Txgy.DataStat.Views
{
/// <summary>
/// UserControlB.xaml 的交互逻辑
/// </summary>
public partial class UserControlB : UserControl
{
public UserControlB()
{
InitializeComponent();
DataContext = App.Current.Services.GetService<UserControlBViewModel>();
}
}
}
Loading…
Cancel
Save