add settings screen

get playlist from settings, in first run - open Settings
This commit is contained in:
Vova
2024-01-30 16:31:11 +02:00
parent d8365d2b1f
commit 9b8e8a835a
18 changed files with 527 additions and 228 deletions
@@ -216,4 +216,82 @@ c-0.781-0.781-0.788-2.047-0.007-2.828L51.438,14.43c1.754-1.755,1.753-4.61-0.001-
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<LinearGradientBrush x:Key="YellowGradient" >
<GradientStop Offset="0" Color="LightGray"/>
<GradientStop Offset="1" Color="Gray"/>
</LinearGradientBrush>
<Style x:Key="{x:Type CheckBox}" TargetType="CheckBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Background" Value="{StaticResource YellowGradient}" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="FontSize" Value="18" />
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="#FF03428f" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Grid FlowDirection="LeftToRight">
<Border Name="Border"
Background="White"
CornerRadius="1"
BorderBrush="#FF92c8e5"
BorderThickness="2"
Width="46"
Height="38"
>
<Border
Name="ShadowBorder"
CornerRadius="2"
Padding="2"
ClipToBounds="True"
Background="Transparent"
BorderBrush="#FF92c8e5"
Margin="-2,-2,0,0"
BorderThickness="2,2,0,0" >
<Border.Effect>
<DropShadowEffect ShadowDepth="3" BlurRadius="7"/>
</Border.Effect>
</Border>
</Border>
<Path x:Name="CheckMark" SnapsToDevicePixels="False" Data="F1M1,14.489C1,14.489 4.963,10.525 4.963,10.525 4.963,10.525 11.328,16.89 11.328,16.89 11.328,16.89 27.034,1.183 27.034,1.183 27.034,1.183 31,5.146 31,5.146 31,5.146 11.328,24.817 11.328,24.817 11.328,24.817 1,14.489 1,14.489z" Fill="#FFFFFFFF" Height="26" Canvas.Left="0" Canvas.Top="0" Width="32"/>
</Grid>
</BulletDecorator.Bullet>
<ContentPresenter Margin="4,0,0,0" TextBlock.Foreground="{TemplateBinding Foreground}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
RecognizesAccessKey="True"/>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{Binding Background,RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" />
<Setter TargetName="ShadowBorder" Property="Visibility" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TextBox" TargetType="TextBox">
<Setter Property="FontFamily" Value="Arial"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="FontSize" Value="25"></Setter>
<Setter Property="Foreground" Value="Gray"></Setter>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="TextAlignment" Value="Left"></Setter>
</Style>
</ResourceDictionary> </ResourceDictionary>
+7 -7
View File
@@ -20,17 +20,17 @@
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Visibility="{Binding IsTopPanelVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}" Height="70"> <Grid Visibility="{Binding IsTopPanelVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}" Height="80">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="80"/>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonGear}" /> <Button Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonGear}" Command="{Binding SettingsCommand}" />
<Button Grid.Column="1" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" /> <Button Grid.Column="1" Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
<TextBlock Grid.Column="2" FontSize="30" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/> <TextBlock Grid.Column="2" FontSize="30" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Grid.Column="4" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" /> <Button Grid.Column="4" Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
</Grid> </Grid>
<ContentControl Grid.Row="1" Name="ControlContainer" Content="{Binding Control}" /> <ContentControl Grid.Row="1" Name="ControlContainer" Content="{Binding Control}" />
</Grid> </Grid>
+18
View File
@@ -0,0 +1,18 @@
<UserControl x:Class="TV_Player.Settings"
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:tv_player="clr-namespace:TV_Player"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel x:Name="groupsGrid" VerticalAlignment="Center">
<Label Margin="10" Foreground="White" FontSize="25" FontWeight="Bold" FontFamily="Arial">Адрес плейлиста</Label>
<TextBox Margin="10" Style="{StaticResource TextBox}" HorizontalAlignment="Stretch" Text="{Binding PlaylistURL}"></TextBox>
<CheckBox Margin="10" Foreground="White" FontSize="25" IsChecked="{Binding StartFullScreen}">Откывать во весь экран</CheckBox>
<CheckBox Margin="10" Foreground="White" FontSize="25" IsChecked="{Binding StartLastScreen}">Запоминать последний выбор</CheckBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Grid.Column="1" Height="70" Width="70" Margin="10,0,50,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
<Button HorizontalAlignment="Center" FontSize="25" Command="{Binding SaveCommand}">Сохранить</Button>
</StackPanel>
</StackPanel>
</UserControl>
+15
View File
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace TV_Player
{
/// <summary>
/// Interaction logic for ProgramsGroupGrid.xaml
/// </summary>
public partial class Settings : UserControl
{
public Settings()
{
InitializeComponent();
}
}
}
+4
View File
@@ -19,6 +19,7 @@
<TreatAsUsed>true</TreatAsUsed> <TreatAsUsed>true</TreatAsUsed>
</PackageReference> </PackageReference>
<PackageReference Include="LibVLCSharp.WPF" Version="3.8.2" /> <PackageReference Include="LibVLCSharp.WPF" Version="3.8.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Reactive" Version="6.0.0" /> <PackageReference Include="System.Reactive" Version="6.0.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" /> <PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
</ItemGroup> </ItemGroup>
@@ -30,6 +31,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Settings.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="VideoPlayer.xaml.cs"> <Compile Update="VideoPlayer.xaml.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
+25 -20
View File
@@ -4,51 +4,56 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF" xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TV_Player"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
Unloaded="UserControl_Unloaded"> Unloaded="UserControl_Unloaded">
<UserControl.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverterKey"/>
</UserControl.Resources>
<Grid> <Grid>
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1"> <vlc:VideoView x:Name="VideoView" Panel.ZIndex="1">
<Grid Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF" VerticalAlignment="Stretch" <Grid Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown"> HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown">
<Grid x:Name="overlayPanel" Visibility="Visible"> <Grid x:Name="overlayPanel" Visibility="Visible">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="70"/> <RowDefinition Height="80"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="70"/> <RowDefinition Height="80"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Background="#70000000" > <Grid Background="#70000000" >
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" /> <Button Height="50" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
<TextBlock Grid.Column="1" FontSize="20" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/> <TextBlock Grid.Column="1" FontSize="20" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Grid.Column="4" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" /> <Button Grid.Column="4" Height="70" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
</Grid> </Grid>
<Grid Grid.Row="2" Background="#70000000"> <Grid Grid.Row="2" Background="#B0000000">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="180"/> <ColumnDefinition Width="180"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.RowSpan="2" Orientation="Horizontal" > <StackPanel Grid.RowSpan="2" Orientation="Horizontal" >
<Button Grid.RowSpan="2" Height="50" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonDown}" Command="{Binding PreviousCommand}" /> <Button Grid.RowSpan="2" Height="70" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonDown}" Command="{Binding PreviousCommand}" />
<TextBlock FontSize="15" Foreground="White" Text="Ch" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock FontSize="15" Foreground="White" Text="Ch" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Grid.RowSpan="2" Height="50" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonUp}" Command="{Binding NextCommand}" /> <Button Grid.RowSpan="2" Height="70" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonUp}" Command="{Binding NextCommand}" />
</StackPanel>
<TextBlock Grid.Column="1" FontSize="20" Foreground="White" Text="{Binding ProgramGuideText}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Column="1" Grid.Row="1">
<TextBlock FontSize="15" Foreground="White" Text="{Binding StartProgram}" HorizontalAlignment="Center" />
<ProgressBar Height="10" Foreground="Yellow" Name="progressBar" Value="{Binding DurationValue}" Maximum="100" VerticalAlignment="Center" HorizontalAlignment="Center" Width="400"/>
<TextBlock FontSize="15" Foreground="White" Text="{Binding EndProgram}" HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
<Grid Grid.Column="1" x:Name="programInfo" Visibility="{Binding IsProgramInfoVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="20" Foreground="White" Text="{Binding ProgramGuideText}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1">
<TextBlock FontSize="15" Foreground="White" Text="{Binding StartProgram}" HorizontalAlignment="Center" />
<ProgressBar Height="10" Foreground="Yellow" Name="progressBar" Value="{Binding DurationValue}" Maximum="100" VerticalAlignment="Center" HorizontalAlignment="Center" Width="400"/>
<TextBlock FontSize="15" Foreground="White" Text="{Binding EndProgram}" HorizontalAlignment="Center" />
</StackPanel>
</Grid>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
+19 -5
View File
@@ -3,6 +3,7 @@ using LibVLCSharp.WPF;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
namespace TV_Player namespace TV_Player
{ {
@@ -16,11 +17,17 @@ namespace TV_Player
private LibVLC _libVLC; private LibVLC _libVLC;
private MediaPlayer _mediaPlayer; private MediaPlayer _mediaPlayer;
private PlayerViewModel _viewModel; private PlayerViewModel _viewModel;
private DispatcherTimer _overlayAutoHideTimer;
public VideoPlayer() public VideoPlayer()
{ {
InitializeComponent(); InitializeComponent();
_overlayAutoHideTimer = new DispatcherTimer();
_overlayAutoHideTimer.Interval = TimeSpan.FromSeconds(3);
_overlayAutoHideTimer.Tick += _overlayAutoHideTimer_Tick;
_overlayAutoHideTimer.Start();
_libVLC = new LibVLC(enableDebugLogs: true); _libVLC = new LibVLC(enableDebugLogs: true);
_mediaPlayer = new MediaPlayer(_libVLC); _mediaPlayer = new MediaPlayer(_libVLC);
this.DataContextChanged += (sender, e) => this.DataContextChanged += (sender, e) =>
@@ -28,7 +35,6 @@ namespace TV_Player
_viewModel = (PlayerViewModel)e.NewValue; _viewModel = (PlayerViewModel)e.NewValue;
_viewModel.SourceUrlChangedEvent += _viewModel_SourceUrlChangedEvent; _viewModel.SourceUrlChangedEvent += _viewModel_SourceUrlChangedEvent;
}; };
VideoView.Loaded += (sender, e) => VideoView.Loaded += (sender, e) =>
{ {
@@ -39,7 +45,7 @@ namespace TV_Player
AutoPlay(); AutoPlay();
}; };
Unloaded += VideoPlayer_Unloaded; Unloaded += VideoPlayer_Unloaded;
} }
private void _viewModel_SourceUrlChangedEvent(string videoURL) private void _viewModel_SourceUrlChangedEvent(string videoURL)
{ {
@@ -82,20 +88,28 @@ namespace TV_Player
private void ToggleOverlay() private void ToggleOverlay()
{ {
if (overlayPanel.Visibility == Visibility.Visible) if (overlayPanel.Visibility == Visibility.Visible)
{ {
HideOverlay(); HideOverlay();
} }
else else
{ {
ShowOverlay(); ShowOverlay();
} }
} }
private void _overlayAutoHideTimer_Tick(object? sender, EventArgs e)
{
HideOverlay();
}
public void ShowOverlay() public void ShowOverlay()
{ {
_overlayAutoHideTimer.Start();
overlayPanel.Visibility = Visibility.Visible; overlayPanel.Visibility = Visibility.Visible;
} }
public void HideOverlay() public void HideOverlay()
{ {
_overlayAutoHideTimer.Stop();
overlayPanel.Visibility = Visibility.Collapsed; overlayPanel.Visibility = Visibility.Collapsed;
} }
private void UserControl_Unloaded(object sender, RoutedEventArgs e) private void UserControl_Unloaded(object sender, RoutedEventArgs e)
+1
View File
@@ -1,5 +1,6 @@
namespace TV_Player namespace TV_Player
{ {
[Serializable]
public class GroupInfo public class GroupInfo
{ {
public string Name { get; set; } public string Name { get; set; }
+103 -112
View File
@@ -1,7 +1,6 @@
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Channels;
using System.Xml; using System.Xml;
namespace TV_Player namespace TV_Player
@@ -28,7 +27,6 @@ namespace TV_Player
public static class M3UParser public static class M3UParser
{ {
public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url) public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url)
{ {
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ; List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ;
@@ -46,7 +44,6 @@ namespace TV_Player
return epgChannels; return epgChannels;
} }
private static List<ProgramGuide> ParseEpg(string epgData) private static List<ProgramGuide> ParseEpg(string epgData)
{ {
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); List<ProgramGuide> epgChannels = new List<ProgramGuide>();
@@ -75,14 +72,14 @@ namespace TV_Player
{ {
ProgramInfo program = new ProgramInfo(); ProgramInfo program = new ProgramInfo();
var id=reader.GetAttribute("channel"); var id = reader.GetAttribute("channel");
var channel = epgChannels.FirstOrDefault(x => x.Id == id); var channel = epgChannels.FirstOrDefault(x => x.Id == id);
program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null); program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
program.StopTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null); program.StopTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
reader.Read(); reader.Read();
program.Title = reader.ReadElementContentAsString(); program.Title = reader.ReadElementContentAsString();
channel.Programs.Add(program); channel.Programs.Add(program);
} }
@@ -92,132 +89,126 @@ namespace TV_Player
} }
} }
} }
catch (Exception ex) { catch (Exception ex)
{
}
} }
}
//using (XmlReader reader = XmlReader.Create(new System.IO.StringReader(epgData)))
//{
// ProgramGuide currentChannel = null;
// XmlDocument doc = new XmlDocument();
// doc.LoadXml(epgData);
// XmlNodeList channelNodes = doc.SelectNodes("//channel");
// foreach (XmlNode channelNode in channelNodes)
// {
// ProgramGuide channel = new ProgramGuide();
// channel.Id = channelNode.Attributes["id"].Value;
// channel.DisplayName = channelNode.SelectSingleNode("display-name").InnerText;
// XmlNodeList programNodes = doc.SelectNodes($"//programme[@channel='{channel.Id}']");
// foreach (XmlNode programNode in programNodes)
// {
// ProgramInfo program = new ProgramInfo();
// program.Title = programNode.SelectSingleNode("title").InnerText;
// program.StartTime = DateTime.ParseExact(programNode.Attributes["start"].Value, "yyyyMMddHHmmss zzz", null);
// program.StopTime = DateTime.ParseExact(programNode.Attributes["stop"].Value, "yyyyMMddHHmmss zzz", null);
// channel.Programs.Add(program);
// }
// epgChannels.Add(channel);
// }
//}
return epgChannels; return epgChannels;
} }
private static async Task<string> ReadFile(string url)
public static async Task<List<M3UInfo>> DownloadM3UFromWebAsync(string url)
{
List<M3UInfo> playlistItems = new List<M3UInfo>();
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{ {
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text")); string responseBody;
request.Method = HttpMethod.Get; using (var client = new HttpClient())
request.RequestUri = new Uri(url); using (var request = new HttpRequestMessage())
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
// Parse M3U content
playlistItems = ParseM3UFromString(responseBody);
}
return playlistItems;
}
static string[] SplitStringBeforeSeparator(string input, string separator)
{
string[] parts = input.Split(separator);
// Reconstruct the string until the separator is reached
int separatorIndex = input.IndexOf(separator);
if (separatorIndex != -1)
{
parts[0] = input.Substring(0, separatorIndex + 1);
for (int i = 1; i < parts.Length; i++)
{ {
parts[i] = separator + parts[i]; client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text"));
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(url);
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
} }
return responseBody;
} }
return parts; public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
}
static List<M3UInfo> ParseM3UFromString(string content)
{
List<M3UInfo> playlistItems = new List<M3UInfo>();
try
{ {
var m3u = SplitStringBeforeSeparator(content, "#EXT"); var fileData=await ReadFile(url);
// Parse M3U content
return ParseM3UFromString(fileData);
}
private static string[] SplitStringBeforeSeparator(string input, string separator)
{
string[] parts = input.Split(separator);
foreach (var line in m3u) // Reconstruct the string until the separator is reached
int separatorIndex = input.IndexOf(separator);
if (separatorIndex != -1)
{ {
if (line.StartsWith("#EXTINF:")) parts[0] = input.Substring(0, separatorIndex + 1);
for (int i = 1; i < parts.Length; i++)
{ {
if (TryParseM3ULine(line, out var m3uInfo)) parts[i] = separator + parts[i];
}
}
return parts;
}
private static (List<M3UInfo> programList, string programGuide) ParseM3UFromString(string content)
{
List<M3UInfo> playlistItems = new List<M3UInfo>();
string programGuideLink = string.Empty;
try
{
var m3u = SplitStringBeforeSeparator(content, "#EXT");
foreach (var line in m3u)
{
if (line.StartsWith("#EXTINF:"))
{ {
playlistItems.Add(m3uInfo); if (TryParseM3ULine(line, out var m3uInfo))
{
playlistItems.Add(m3uInfo);
}
}
if (line.StartsWith("#EXTM3U"))
{
programGuideLink=ExtractXtvgUrl(line);
} }
} }
} }
} catch (Exception ex)
catch (Exception ex)
{
Console.WriteLine("Error reading M3U file: " + ex.Message);
}
return playlistItems;
}
static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
{
info = null;
string pattern = @"#EXTINF:\d+ CUID=""(?<CUID>.*?)"" number=""(?<Number>.*?)"" tvg-id=""(?<TvgID>.*?)"" tvg-name=""(?<TvgName>.*?)"".*?tvg-logo=""(?<Logo>.*?)"" group-title=""(?<GroupTitle>.*?)""[^,]*,(?<Name>.*)[^\r](?<URL>.*)$";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
Match match = regex.Match(m3uLine);
if (match.Success)
{
info = new M3UInfo
{ {
CUID = match.Groups["CUID"].Value, Console.WriteLine("Error reading M3U file: " + ex.Message);
Number = match.Groups["Number"].Value, }
TvgID = match.Groups["TvgID"].Value,
TvgName = match.Groups["TvgName"].Value, return (playlistItems,programGuideLink);
GroupTitle = match.Groups["GroupTitle"].Value,
Logo = match.Groups["Logo"].Value,
Name = match.Groups["Name"].Value,
Url = match.Groups["URL"].Value
};
return true;
} }
return false; private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
{
info = null;
string pattern = @"#EXTINF:\d+ CUID=""(?<CUID>.*?)"" number=""(?<Number>.*?)"" tvg-id=""(?<TvgID>.*?)"" tvg-name=""(?<TvgName>.*?)"".*?tvg-logo=""(?<Logo>.*?)"" group-title=""(?<GroupTitle>.*?)""[^,]*,(?<Name>.*)[^\r](?<URL>.*)$";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
Match match = regex.Match(m3uLine);
if (match.Success)
{
info = new M3UInfo
{
CUID = match.Groups["CUID"].Value,
Number = match.Groups["Number"].Value,
TvgID = match.Groups["TvgID"].Value,
TvgName = match.Groups["TvgName"].Value,
GroupTitle = match.Groups["GroupTitle"].Value,
Logo = match.Groups["Logo"].Value,
Name = match.Groups["Name"].Value,
Url = match.Groups["URL"].Value
};
return true;
}
return false;
}
private static string ExtractXtvgUrl(string m3uEntry)
{
// Define a regular expression pattern to match x-tvg-url attribute
string pattern = @"x-tvg-url=""(.*?)""";
// Use Regex.Match to find the first match
Match match = Regex.Match(m3uEntry, pattern);
// Check if a match is found and get the value from the capturing group
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value;
}
// Return null or an empty string if no match is found
return string.Empty;
}
} }
} }
}
@@ -2,6 +2,7 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using TV_Player.ViewModels;
namespace TV_Player namespace TV_Player
{ {
@@ -48,11 +49,14 @@ namespace TV_Player
public Action ButtonBackAction { get; set; } public Action ButtonBackAction { get; set; }
public ICommand BackCommand { get; } public ICommand BackCommand { get; }
public ICommand SettingsCommand{ get; }
public MainViewModel() public MainViewModel()
{ {
BackCommand = new RelayCommand(OnButtonBackClick); BackCommand = new RelayCommand(OnButtonBackClick);
FullscreenCommand = new RelayCommand(OnFullSctreenButtonClick); FullscreenCommand = new RelayCommand(OnFullSctreenButtonClick);
SettingsCommand = new RelayCommand(OnSettingsButtonClick);
CurrentWindowStyle = WindowStyle.SingleBorderWindow; CurrentWindowStyle = WindowStyle.SingleBorderWindow;
} }
@@ -76,5 +80,10 @@ namespace TV_Player
ButtonBackAction?.Invoke(); ButtonBackAction?.Invoke();
} }
private void OnSettingsButtonClick()
{
TVPlayerViewModel.Instance.ShowSettingsScreen();
}
} }
} }
+31 -14
View File
@@ -11,7 +11,14 @@ namespace TV_Player
public delegate void SourceUrlChanged(string videoURL); public delegate void SourceUrlChanged(string videoURL);
public event SourceUrlChanged SourceUrlChangedEvent; public event SourceUrlChanged SourceUrlChangedEvent;
private M3UInfo _currentProgram; private M3UInfo _currentProgram;
private bool _isProgramInfoVisible;
public bool IsProgramInfoVisible
{
get => _isProgramInfoVisible;
set => SetProperty(ref _isProgramInfoVisible, value);
}
private string _topPaneTitle; private string _topPaneTitle;
public string TopPanelTitle public string TopPanelTitle
@@ -71,7 +78,7 @@ namespace TV_Player
PreviousCommand = new RelayCommand(PreviousProgram); PreviousCommand = new RelayCommand(PreviousProgram);
FullscreenCommand = new RelayCommand(TVPlayerViewModel.Instance.FullScreenToggle); FullscreenCommand = new RelayCommand(TVPlayerViewModel.Instance.FullScreenToggle);
_programSubscriber = ProgramsData.Instance.AllPrograms.Subscribe(x => _programSubscriber = TVPlayerViewModel.Instance.PlaylistData.AllPrograms.Subscribe(x =>
{ {
_programs = x.Where(p => p.GroupTitle == _currentProgram.GroupTitle).ToList(); _programs = x.Where(p => p.GroupTitle == _currentProgram.GroupTitle).ToList();
_currentProgramIndex = _programs.Select((program, index) => new { program, index }) _currentProgramIndex = _programs.Select((program, index) => new { program, index })
@@ -79,7 +86,7 @@ namespace TV_Player
.Select(x => x.index) .Select(x => x.index)
.FirstOrDefault(); .FirstOrDefault();
}); });
UpdateUI(); UpdateUI();
} }
@@ -89,7 +96,7 @@ namespace TV_Player
TopPanelTitle = _currentProgram.Name; TopPanelTitle = _currentProgram.Name;
SourceUrlChangedEvent?.Invoke(_currentProgram.Url); SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
_programGuideDisposable = ProgramsData.Instance.ProgramGuideInfo.Subscribe(x => _programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(x =>
{ {
try try
{ {
@@ -125,23 +132,33 @@ namespace TV_Player
} }
private void UpdateScreenInfo() private void UpdateScreenInfo()
{ {
_currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now); try
if (_currentProgramInfo.Title != ProgramGuideText)
{ {
ProgramGuideText = _currentProgramInfo.Title; _currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now);
StartProgram = _currentProgramInfo.StartTime.ToShortTimeString(); if (_currentProgramInfo == null)
EndProgram = _currentProgramInfo.StopTime.ToShortTimeString(); {
IsProgramInfoVisible = false;
}
else if (_currentProgramInfo.Title != ProgramGuideText)
{
IsProgramInfoVisible = true;
ProgramGuideText = _currentProgramInfo.Title;
StartProgram = _currentProgramInfo.StartTime.ToShortTimeString();
EndProgram = _currentProgramInfo.StopTime.ToShortTimeString();
var programMinutes = (_currentProgramInfo.StopTime - _currentProgramInfo.StartTime).TotalMinutes;
DurationValue = (int)((DateTime.Now - _currentProgramInfo.StartTime).TotalMinutes / programMinutes * 100);
}
} }
var programMinutes = (_currentProgramInfo.StopTime - _currentProgramInfo.StartTime).TotalMinutes; catch
DurationValue = (int)((DateTime.Now - _currentProgramInfo.StartTime).TotalMinutes / programMinutes * 100); { }
} }
private void OnButtonBackClick() private void OnButtonBackClick()
{ {
var groupInfo = new GroupInfo() { Name = _currentProgram.GroupTitle, Count = 0 }; var groupInfo = new GroupInfo() { Name = _currentProgram.GroupTitle, Count = 0 };
var programListViewModel = new ProgramsListViewModel(groupInfo); TVPlayerViewModel.Instance.ShowProgramsListScreen(groupInfo);
var conrtrol = new ProgramsList();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
} }
public void Dispose() public void Dispose()
{ {
+18 -22
View File
@@ -4,16 +4,6 @@ namespace TV_Player
{ {
public class ProgramsData public class ProgramsData
{ {
private static ProgramsData _instance;
public static ProgramsData Instance
{
get
{
_instance ??= new ProgramsData();
return _instance;
}
}
private readonly ReplaySubject<List<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>(); private readonly ReplaySubject<List<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>();
private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>(); private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>();
private readonly ReplaySubject<List<ProgramGuide>> programGuideSubject = new ReplaySubject<List<ProgramGuide>>(); private readonly ReplaySubject<List<ProgramGuide>> programGuideSubject = new ReplaySubject<List<ProgramGuide>>();
@@ -21,29 +11,35 @@ namespace TV_Player
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject; public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject; public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
private ProgramsData() public ProgramsData()
{ {
Task.Run(() => GetPrograms());
Task.Run(() => GetProgramGuide());
} }
private async Task GetPrograms()
private async Task GetPrograms(string m3uLink)
{ {
string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u"; //string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
var programs = await M3UParser.DownloadM3UFromWebAsync(m3uLink); var result = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
programsSubject.OnNext(result.programList);
programsSubject.OnNext(programs); var groupping = result.programList.GroupBy(item => item.GroupTitle)
var groupping = programs.GroupBy(item => item.GroupTitle)
.Select(group => new GroupInfo() { Name = group.Key, Count = group.Count() }) .Select(group => new GroupInfo() { Name = group.Key, Count = group.Count() })
.ToList(); .ToList();
groupsSubject.OnNext(groupping); groupsSubject.OnNext(groupping);
await Task.Run(() => GetProgramGuide(result.programGuide));
} }
private async Task GetProgramGuide() private async Task GetProgramGuide(string guideLink)
{ {
string guideLink = "http://epg.da-tv.vip/107-light.xml"; //string guideLink = "http://epg.da-tv.vip/107-light.xml";
var programGuide = await M3UParser.DownloadGuideFromWebAsync(guideLink); var programGuide = await M3UParser.DownloadGuideFromWebAsync(guideLink);
programGuideSubject.OnNext(programGuide); programGuideSubject.OnNext(programGuide);
} }
internal void GetData(string playlistURL)
{
Task.Run(() => GetPrograms(playlistURL));
}
} }
} }
@@ -20,16 +20,14 @@ namespace TV_Player
public ProgramsGroupViewModel() public ProgramsGroupViewModel()
{ {
ItemSelectedCommand = new RelayCommand(OnItemSelected); ItemSelectedCommand = new RelayCommand(OnItemSelected);
_groupInformationSubscriber = ProgramsData.Instance.GroupsInformation.Subscribe(x=>Programs = x); _groupInformationSubscriber = TVPlayerViewModel.Instance.PlaylistData.GroupsInformation.Subscribe(x=>Programs = x);
TVPlayerViewModel.Instance.TopPanelVisible(true, "Группы"); TVPlayerViewModel.Instance.TopPanelVisible(true, "Группы");
} }
private void OnItemSelected() private void OnItemSelected()
{ {
var programListViewModel = new ProgramsListViewModel(SelectedItem); TVPlayerViewModel.Instance.ShowProgramsListScreen(SelectedItem);
var conrtrol = new ProgramsList();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
} }
public void Dispose() public void Dispose()
@@ -16,26 +16,22 @@ namespace TV_Player
public M3UInfo SelectedItem { get; set; } public M3UInfo SelectedItem { get; set; }
public ICommand ItemSelectedCommand { get; } public ICommand ItemSelectedCommand { get; }
private IDisposable _programSubscriber; private IDisposable _programSubscriber;
public ProgramsListViewModel(GroupInfo groupInfo) public ProgramsListViewModel(GroupInfo groupInfo)
{ {
TVPlayerViewModel.Instance.TopPanelVisible(true, groupInfo.Name); TVPlayerViewModel.Instance.TopPanelVisible(true, groupInfo.Name);
ItemSelectedCommand = new RelayCommand(OnItemSelected); ItemSelectedCommand = new RelayCommand(OnItemSelected);
_programSubscriber = ProgramsData.Instance.AllPrograms.Subscribe(x => Programs = x.Where(p => p.GroupTitle == groupInfo.Name).ToList()); _programSubscriber = TVPlayerViewModel.Instance.PlaylistData.AllPrograms.Subscribe(x => Programs = x.Where(p => p.GroupTitle == groupInfo.Name).ToList());
TVPlayerViewModel.Instance.SetBackButtonAction(new Action(() => TVPlayerViewModel.Instance.SetBackButtonAction(new Action(() =>
{ {
var programGroupViewModel = new ProgramsGroupViewModel(); TVPlayerViewModel.Instance.ShowProgramsGroupScreen();
var conrtrol = new ProgramsGroupGrid();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programGroupViewModel);
})); }));
} }
private void OnItemSelected() private void OnItemSelected()
{ {
var playerViewModel = new PlayerViewModel(SelectedItem); TVPlayerViewModel.Instance.ShowPlayerScreen(SelectedItem);
var conrtrol = new VideoPlayer();
conrtrol.SourceUrl = SelectedItem.Url;
TVPlayerViewModel.Instance.SetPageContext(conrtrol, playerViewModel);
} }
public void Dispose() public void Dispose()
-33
View File
@@ -1,33 +0,0 @@
using System.Windows.Input;
namespace TV_Player.ViewModels
{
//public class RelayCommand : ICommand
//{
// private readonly Action _execute;
// private readonly Func<bool> _canExecute;
// public event EventHandler CanExecuteChanged;
// public RelayCommand(Action execute, Func<bool> canExecute = null)
// {
// _execute = execute ?? throw new ArgumentNullException(nameof(execute));
// _canExecute = canExecute;
// }
// public bool CanExecute(object parameter)
// {
// return _canExecute == null || _canExecute();
// }
// public void Execute(object parameter)
// {
// _execute();
// }
// public void RaiseCanExecuteChanged()
// {
// CanExecuteChanged?.Invoke(this, EventArgs.Empty);
// }
//}
}
+64
View File
@@ -0,0 +1,64 @@
using Newtonsoft.Json;
using System.IO;
namespace TV_Player.ViewModels
{
public static class SettingsModel
{
private const string _filePath = "settings.json";
public static string PlaylistURL { get; set; }
public static bool StartFullScreen { get; set; }
public static bool StartFromLastScreen { get; set; }
public static string LastScreen { get; set; }
public static GroupInfo Group { get; set; }
public static M3UInfo Program { get; set; }
public static void SaveSetttings()
{
// Create an anonymous object to hold the properties
var dataToSerialize = new
{
PlaylistURL,
StartFromLastScreen,
StartFullScreen,
LastScreen,
Group,
Program
};
// Serialize the object to JSON
string json = JsonConvert.SerializeObject(dataToSerialize, Formatting.Indented);
// Save the JSON to a file
File.WriteAllText(_filePath, json);
}
public static void LoadSettings()
{
var loadedData = new
{
PlaylistURL = default(string),
LastScreen = default(string),
Group = default(GroupInfo),
Program = default(M3UInfo),
StartFromLastScreen = default(bool),
StartFullScreen = default(bool)
};
if (File.Exists(_filePath))
{
// Read the JSON content from the file
string json = File.ReadAllText(_filePath);
loadedData = JsonConvert.DeserializeAnonymousType(json, loadedData);
}
// Assign the values to the properties
PlaylistURL = loadedData.PlaylistURL;
LastScreen = loadedData.LastScreen;
Group = loadedData.Group;
Program = loadedData.Program;
StartFromLastScreen = loadedData.StartFromLastScreen;
StartFullScreen = loadedData.StartFullScreen;
}
}
}
@@ -0,0 +1,59 @@
using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;
namespace TV_Player.ViewModels
{
internal class SettingsViewModel : ObservableViewModelBase
{
private string _playlistURL;
public string PlaylistURL
{
get => _playlistURL;
set => SetProperty(ref _playlistURL, value);
}
private bool _startFullScreen;
public bool StartFullScreen
{
get => _startFullScreen;
set => SetProperty(ref _startFullScreen, value);
}
private bool _startLastScreen;
public bool StartLastScreen
{
get => _startLastScreen;
set => SetProperty(ref _startLastScreen, value);
}
public ICommand SaveCommand { get; }
public ICommand BackCommand { get; }
public SettingsViewModel()
{
TVPlayerViewModel.Instance.TopPanelVisible(false, "");
SaveCommand = new RelayCommand(OnSaveSettings);
BackCommand = new RelayCommand(OnBackCommand);
StartFullScreen = SettingsModel.StartFullScreen;
StartLastScreen = SettingsModel.StartFromLastScreen;
PlaylistURL = SettingsModel.PlaylistURL;
}
private void OnBackCommand()
{
TVPlayerViewModel.Instance.SelectScreen();
}
private void OnSaveSettings()
{
SettingsModel.StartFullScreen = StartFullScreen;
SettingsModel.StartFromLastScreen = StartLastScreen;
SettingsModel.PlaylistURL = PlaylistURL;
SettingsModel.SaveSetttings();
TVPlayerViewModel.Instance.InitializeTVWithData();
}
}
}
+70 -3
View File
@@ -5,6 +5,7 @@ namespace TV_Player.ViewModels
public class TVPlayerViewModel : IDisposable public class TVPlayerViewModel : IDisposable
{ {
private readonly MainViewModel _mainViewModel; private readonly MainViewModel _mainViewModel;
public ProgramsData PlaylistData { get; private set; }
public Action ButtonBackAction { get; set; } public Action ButtonBackAction { get; set; }
@@ -21,6 +22,8 @@ namespace TV_Player.ViewModels
public TVPlayerViewModel() public TVPlayerViewModel()
{ {
PlaylistData = new ProgramsData();
_mainViewModel = new MainViewModel(); _mainViewModel = new MainViewModel();
var mainWindow = new MainWindow(); var mainWindow = new MainWindow();
mainWindow.DataContext = _mainViewModel; mainWindow.DataContext = _mainViewModel;
@@ -28,19 +31,81 @@ namespace TV_Player.ViewModels
mainWindow.Show(); mainWindow.Show();
_instance = this; _instance = this;
ShowInitialScreen(); SettingsModel.LoadSettings();
InitializeTVWithData();
} }
private void ShowInitialScreen() public void InitializeTVWithData()
{
if (!string.IsNullOrEmpty(SettingsModel.PlaylistURL))
{
if (SettingsModel.StartFullScreen)
FullScreenToggle();
PlaylistData.GetData(SettingsModel.PlaylistURL);
if (SettingsModel.StartFromLastScreen)
SelectScreen();
else
ShowProgramsGroupScreen();
}
else
{
ShowSettingsScreen();
}
}
public void SelectScreen()
{
switch (SettingsModel.LastScreen)
{
case "ProgramsListViewModel":
ShowProgramsListScreen(SettingsModel.Group);
break;
case "PlayerViewModel":
ShowPlayerScreen(SettingsModel.Program);
break;
default:
ShowProgramsGroupScreen();
break;
}
}
public void ShowProgramsGroupScreen()
{ {
var vm = new ProgramsGroupViewModel(); var vm = new ProgramsGroupViewModel();
var control = new ProgramsGroupGrid(); var control = new ProgramsGroupGrid();
control.DataContext = vm; control.DataContext = vm;
SettingsModel.LastScreen = nameof(ProgramsGroupViewModel);
SetPageContext(control, vm); SetPageContext(control, vm);
} }
public void ShowProgramsListScreen(GroupInfo group)
{
SettingsModel.Group = group;
var programListViewModel = new ProgramsListViewModel(group);
var conrtrol = new ProgramsList();
SettingsModel.LastScreen = nameof(ProgramsListViewModel);
SetPageContext(conrtrol, programListViewModel);
}
public void ShowPlayerScreen(M3UInfo program)
{
SettingsModel.Program = program;
var playerViewModel = new PlayerViewModel(program);
var conrtrol = new VideoPlayer();
conrtrol.SourceUrl = program.Url;
SettingsModel.LastScreen = nameof(PlayerViewModel);
SetPageContext(conrtrol, playerViewModel);
}
public void ShowSettingsScreen()
{
var playerViewModel = new SettingsViewModel();
var conrtrol = new Settings();
SetPageContext(conrtrol, playerViewModel);
}
public void TopPanelVisible(bool value, string title) public void TopPanelVisible(bool value, string title)
{ {
_mainViewModel.IsTopPanelVisible = value; _mainViewModel.IsTopPanelVisible = value;
@@ -57,13 +122,15 @@ namespace TV_Player.ViewModels
_mainViewModel.ButtonBackAction = action; _mainViewModel.ButtonBackAction = action;
} }
public void SetPageContext(ContentControl control, object viewModel) private void SetPageContext(ContentControl control, object viewModel)
{ {
if (_mainViewModel.Control is IDisposable disposable) if (_mainViewModel.Control is IDisposable disposable)
disposable.Dispose(); disposable.Dispose();
control.DataContext = viewModel; control.DataContext = viewModel;
_mainViewModel.Control = control; _mainViewModel.Control = control;
SettingsModel.SaveSetttings();
} }
public void Dispose() public void Dispose()