How to keep trck of transcriptioncomming from Azure - wpf

I have an audio file in mp3 format, that internally I convert into WAV, when everything is done, I send it to the AI. I notice, that the time of transcription varies depending on different factors (length of audio, the region of your service, latency, internet speed, etc).
So how can I add a progress bar to my application, that keeps track accurately, and display the percentage?
I was researching, and I found a method that does this, but for files being uploaded to Azure Blob, I think is easier because you can calculate the file.size. and length.
What about transcriptions, that there is no way for us to determine things?
private async void AzureActionAsync(object obj)
{
var str = obj as string;
if (string.IsNullOrEmpty(str))
{
return;
}
switch (str)
{
case "AudioTranscription":
const string ext = ".wav";
var dlg = new OpenFileDialog
{
DefaultExt = ".mp3",
Filter = "Audio files (.mp3)|*.mp3"
};
var res = dlg.ShowDialog();
if (res! == true)
{
var AudioName = Path.GetFileNameWithoutExtension(dlg.SafeFileName);
var projectPath = Directory.GetParent(Directory.GetCurrentDirectory())?.Parent?.Parent?.FullName;
var FoderName = Path.Combine(projectPath!, "Audios");
var filePath = Path.Combine(FoderName, $"{AudioName}{ext}");
using var mp3 = new Mp3FileReader(dlg.FileName);
using var ws = WaveFormatConversionStream.CreatePcmStream(mp3);
WaveFileWriter.CreateWaveFile(filePath, ws);
await ConvertToTextAsync(filePath);
}
break;
}
}
private async Task ConvertToTextAsync(string FilePath)
{
// Configure speech service
var config = SpeechConfig.FromSubscription(Config.Constants.AZURE_KEY, Config.Constants.AZURE_REGION);
// Configure speech recognition
var taskCompleteionSource = new TaskCompletionSource<int>();
using var audioConfig = AudioConfig.FromWavFileInput(FilePath);
using var speechRecognizer = new SpeechRecognizer(config, audioConfig);
speechRecognizer.Recognizing += SpeechRecognizer_Recognizing;
speechRecognizer.Recognized += SpeechRecognizer_Recognized;
speechRecognizer.SessionStarted += SpeechRecognizer_SessionStarted;
speechRecognizer.SessionStopped += SpeechRecognizer_SessionStopped;
await speechRecognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
Task.WaitAny(new[] { taskCompleteionSource.Task });
await speechRecognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}
private void SpeechRecognizer_SessionStopped(object? sender, SessionEventArgs e)
{
var sb = new StringBuilder();
foreach (var item in Words)
{
sb.Append(item);
}
BackgroundClipboard.SetText(sb.ToString());
if (!string.IsNullOrEmpty(BackgroundClipboard.GetText()))
{
Application.Current.Dispatcher.Invoke(() =>
{
var spellWindow = new SpellCheckWindow();
spellWindow.ShowDialog();
});
}
}
private void SpeechRecognizer_SessionStarted(object? sender, SessionEventArgs e)
{
}
private void SpeechRecognizer_Recognized(object? sender, SpeechRecognitionEventArgs e)
{
if (e.Result.Reason == ResultReason.RecognizedSpeech)
{
foreach (var item in e.Result.Text)
{
Words.Add(item);
}
}
}
private void SpeechRecognizer_Recognizing(object? sender, SpeechRecognitionEventArgs e)
{
}
}
<Border Background="#272537" CornerRadius="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Button
HorizontalAlignment="Right"
materialDesign:RippleAssist.Feedback="Transparent"
Background="{x:Null}"
BorderBrush="{x:Null}"
Command="{Binding ExitCommand}"
Focusable="False">
<materialDesign:PackIcon Kind="Close" />
</Button>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Margin="40" Columns="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<syncfusion:SfHubTile Background="{Binding TileColor}" IsEnabled="{Binding IsTileActive}">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="Click">
<behaviors:InvokeCommandAction
Command="{Binding TileCommand}"
CommandParameter="{Binding TileTitle}"
PassEventArgsToCommand="True" />
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
<Label
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="{Binding TileIcon}"
FontFamily="{StaticResource Material}"
FontSize="120"
Foreground="White" />
</syncfusion:SfHubTile>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Border>
and where in my UI I can thank you for the help this

Related

How to update a TextBlock in ShellView when property changes in Model

I am having trouble updating a ShellView TextBlock with the FirstName property of the LoggedInUserProfile which is created as a Singleton after the user has logged in.
I have a UserProfileView and this binds and updates OK, but the ShellView (which contains the UserProfileView) does not. If I put breakpoints in I can see the LoggedInUserProfile has got correct data.
This is my first WPF app and I have spent a week running myself in circles and trying to figure out what I am doing wrong, but to know avail, hence I am reaching out for guidance.
I dont know but I suspect I am not handling an event properly, not binding correctly or have done something wrong with DI.
Below I have provided the code from what I think are the main components.
What I want to have happen is the First Name of the logged in user is displayed inthe TextBlock of the ShellView after the user has Logged in as well as in the UserProfileView.
Any help you can offer would be appreciated to point me in the right direction. I have include what I think are the main components below.
ShellViewModel
using Caliburn.Micro;
using CRMDesktopUI.EventModels;
using CRMDesktopUI.Library.Models;
using System.Threading;
using System.Threading.Tasks;
namespace CRMDesktopUI.ViewModels
{
public class ShellViewModel:Conductor<object>, IHandle<LogOnEvent>
{
private IEventAggregator _events;
private SimpleContainer _container;
private LoginViewModel _loginVM;
private UserProfileViewModel _userProfileVM;
private ILoggedInUserModel _loggedInUserModel;
public ShellViewModel(LoginViewModel loginVM, IEventAggregator events,ILoggedInUserModel loggedInUserModel, UserProfileViewModel userProfileVM,SimpleContainer container)
{
_events = events;
_loginVM = loginVM;
_userProfileVM = userProfileVM;
_container = container;
_loggedInUserModel = loggedInUserModel;
_events.SubscribeOnUIThread(this);
ActivateItemAsync(_loginVM);
}
Task IHandle<LogOnEvent>.HandleAsync(LogOnEvent message,CancellationToken cancellationToken)
{
_loginVM = _container.GetInstance<LoginViewModel>();
ActivateItemAsync(_userProfileVM);
return Task.CompletedTask;
}
public string FirstName
{
get //This gets called before log in screen activated
{
if(_loggedInUserModel == null)
{
return "Not logged in";
}
else
{
return _loggedInUserModel.FirstName;
}
}
set //Set never gets called
{
_loggedInUserModel.FirstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
}
}
ShellView.xaml
<Window x:Class="CRMDesktopUI.Views.ShellView"
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:local="clr-namespace:CRMDesktopUI.Views"
xmlns:viewmodels="clr-namespace:CRMDesktopUI.ViewModels"
mc:Ignorable="d"
Width="1250" Height="600"
Background="#36393F"
ResizeMode="CanResizeWithGrip"
AllowsTransparency="True"
WindowStyle="None">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.ColumnSpan="2"
Background="#252525"
MouseDown="Border_MouseDown">
<Grid HorizontalAlignment="Stretch">
<Label Content="Test App"
Foreground="Gray"
FontWeight="SemiBold"
FontFamily="/Fonts/#Poppins"/>
<StackPanel HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Width="20" Height="20"
Content="🗕"
Background="Transparent"
BorderThickness="0"
Foreground="Gray"
FontWeight="Bold"
Margin=" 0 0 0 3"
Click="MinimiseButton_Click"/>
<Button Width="20" Height="20"
Content="â–¡"
Background="Transparent"
BorderThickness="0"
Foreground="Gray"
FontWeight="Bold"
Click="MaximiseButton_Click"/>
<Button Width="20" Height="20"
Content="✕"
Background="Transparent"
BorderThickness="0"
Foreground="Gray"
FontWeight="Bold"
Click="CloseButton_Click"/>
</StackPanel>
</Grid>
</Border>
<Grid Background="#2F3136"
Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Label Content="Contacts"
VerticalAlignment="Center"
FontWeight="Medium"
Foreground="Gray"
Margin="8 0 0 0" />
<StackPanel Grid.Row="2"
Orientation="Horizontal"
Background="#292B2f">
<Border CornerRadius="25"
Width="30"
Height="30"
Background="#3bff6f"
Margin="18 0 10 0" />
<DockPanel VerticalAlignment="Center">
<TextBlock Text="First Name:"
Foreground="White"
FontWeight="Light"/>
<TextBlock Text="{Binding FirstName}" <== This does not update the textblock
TextAlignment="Right"
Foreground="White"
FontWeight="SemiBold"
Margin="5 0 10 0" />
</DockPanel>
</StackPanel>
</Grid>
<Grid Grid.Column="1" Grid.Row="1">
<ContentControl x:Name="ActiveItem"
Margin="20" />
</Grid>
</Grid>
</Window>
LoggedInUserModel
namespace CRMDesktopUI.Library.Models
{
public class LoggedInUserModel:ILoggedInUserModel
{
public string Token { get; set; }
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
}
}
LoginViewModel
using Caliburn.Micro;
using CRMDesktopUI.EventModels;
using CRMDesktopUI.Library.Api;
using System;
using System.Threading.Tasks;
namespace CRMDesktopUI.ViewModels
{
public class LoginViewModel:Screen
{
private string _username;
private string _password;
private IAPIHelper _apiHelper;
private string _errormessage;
private IEventAggregator _events;
public LoginViewModel(IAPIHelper apiHelper,IEventAggregator events)
{
_apiHelper = apiHelper;
_events = events;
}
public string ErrorMessage {
get {
return _errormessage;
}
set {
_errormessage = value;
NotifyOfPropertyChange(() => IsErrorVisible);
NotifyOfPropertyChange(() => ErrorMessage);
}
}
public string UserName {
get {
return _username;
}
set {
_username = value;
NotifyOfPropertyChange(() => UserName);
NotifyOfPropertyChange(() => CanLogIn);
}
}
public string Password {
get {
return _password;
}
set {
_password = value;
NotifyOfPropertyChange(() => Password);
NotifyOfPropertyChange(() => CanLogIn);
}
}
public bool CanLogIn {
get {
bool output = false;
if(UserName?.Length > 0 && Password?.Length > 0)
{
output = true;
}
return output;
}
}
public bool IsErrorVisible {
get {
bool output = false;
if(ErrorMessage?.Length > 0)
{
output = true;
}
return output;
}
}
public async Task LogIn()
{
try
{
ErrorMessage = "";
var result = await _apiHelper.Authenticate(UserName,Password);
//get more information about the logged in user
await _apiHelper.GetLogedInUserInfo(result.Access_Token);
await _events.PublishOnUIThreadAsync(new LogOnEvent());
}
catch(Exception ex)
{
ErrorMessage = ex.Message;
}
}
}
}
UserProfileViewModel
using Caliburn.Micro;
using CRMDesktopUI.Library.Models;
namespace CRMDesktopUI.ViewModels
{
public class UserProfileViewModel : Screen
{
ILoggedInUserModel _loggedInUserModel;
public UserProfileViewModel(ILoggedInUserModel loggedInUserModel)
{
_loggedInUserModel = loggedInUserModel;
}
public string FirstName
{
get //This gets called after user logs in
{
return _loggedInUserModel.FirstName;
}
set //This never gets called
{
_loggedInUserModel.FirstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
}
}
UserProfileView
<UserControl x:Class="CRMDesktopUI.Views.UserProfileView"
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:CRMDesktopUI.Views"
xmlns:viewmodels="clr-namespace:CRMDesktopUI.ViewModels"
d:DataContext="{d:DesignInstance Type=viewmodels:UserProfileViewModel}"
mc:Ignorable="d"
Width="800" Height="450">
<StackPanel>
<TextBlock Text="User Profile"
Foreground="White"
FontSize="28"
HorizontalAlignment="Left"
Margin="0 0 0 20"/>
<StackPanel Orientation="Horizontal">
<Border Width="800"
Height="200">
<Border.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,2">
<GradientStop Color="#5bc3ff"
Offset="0.0"/>
<GradientStop Color="#3aa0ff"
Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Border.Clip>
<RectangleGeometry RadiusX="10"
RadiusY="10"
Rect="0 0 800 200"/>
</Border.Clip>
<Grid>
<StackPanel>
<TextBlock Text="{Binding FirstName}"
Foreground="White"
FontSize="28"
Margin="20 10 10 0"/>
</StackPanel>
<Image Width="150"
Height="180"
Source="/Images/822739_user_512x512.png"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Margin="0,0,-39,-31"
RenderTransformOrigin="0.804,0.953">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="0"/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
</Grid>
</Border>
</StackPanel>
</StackPanel>
</UserControl>
HelperClass
using CRMDesktopUI.Library.Models;
using CRMDesktopUI.Models;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace CRMDesktopUI.Library.Api
{
public class APIHelper:IAPIHelper
{
private HttpClient apiClient;
private ILoggedInUserModel _loggedInUser;
public APIHelper(ILoggedInUserModel loggedInUser)
{
InitialiseClient();
_loggedInUser = loggedInUser;
}
private void InitialiseClient()
{
string api = ConfigurationManager.AppSettings["api"];
apiClient = new HttpClient();
apiClient.BaseAddress = new Uri(api);
apiClient.DefaultRequestHeaders.Accept.Clear();
apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<AuthenticatedUser> Authenticate(string username,string password)
{
var data = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password)
});
using(var response = await apiClient.PostAsync("/Token",data))
{
if(response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<AuthenticatedUser>();
return result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
public async Task GetLogedInUserInfo(string token)
{
apiClient.DefaultRequestHeaders.Clear();
apiClient.DefaultRequestHeaders.Accept.Clear();
apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
apiClient.DefaultRequestHeaders.Add("Authorization",$"Bearer {token}");
using(var response = await apiClient.GetAsync("/Api/User"))
{
if(response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<LoggedInUserModel>();
_loggedInUser.Token = token.ToString();
_loggedInUser.Id = result.Id;
_loggedInUser.FirstName = result.FirstName;
_loggedInUser.LastName = result.LastName;
_loggedInUser.EmailAddress = result.EmailAddress;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}
}
Bootstrapper
using Caliburn.Micro;
using CRMDesktopUI.Helpers;
using CRMDesktopUI.Library.Api;
using CRMDesktopUI.Library.Models;
using CRMDesktopUI.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace CRMDesktopUI
{
public class Bootstrapper:BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
ConventionManager.AddElementConvention<PasswordBox>(
PasswordBoxHelper.BoundPasswordProperty,
"Password",
"PasswordChanged");
}
protected override void Configure()
{
_container.Instance(_container);
_container
.Singleton<IWindowManager,WindowManager>()
.Singleton<IEventAggregator,EventAggregator>()
.Singleton<ILoggedInUserModel,LoggedInUserModel>()
.Singleton<IAPIHelper, APIHelper>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(viewModelType,viewModelType.ToString(),viewModelType));
}
protected override void OnStartup(object sender,StartupEventArgs e)
{
DisplayRootViewFor<MainViewModel>();
}
protected override object GetInstance(Type service,string key)
{
return _container.GetInstance(service,key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[] { Assembly.GetExecutingAssembly() };
}
}
}
Once you have successfully logged, you need to Notify that the FirstName property has changed.
Task IHandle<LogOnEvent>.HandleAsync(LogOnEvent message,CancellationToken cancellationToken)
{
_loginVM = _container.GetInstance<LoginViewModel>(); // Not sure why you do this.
ActivateItemAsync(_userProfileVM);
// Since User has logged now, you need to notify change in FirstName
NotifyOfPropertyChange(nameof(FirstName));
return Task.CompletedTask;
}
This would ensure that the ShellView knows that the FirstName property has changed.
Alternatively you could subscribe to the PropertyNotifyChanges of LoginViewModel and filter out the changes for FirstName, however, since you only need the first name after login is successful, the LogOnEvent might be more suitable place.
Also note that you could make the FirstName property readonly as it is most likely not to be edited by the view.
public string FirstName => _loggedInUserModel.FirstName;

APPCRASH on wpfgfx_v0400.dll only on 32-bit systems

I have an issue with a WPF application that I'm writing. I have a window that I load with profile pictures for the user to choose from when setting up an account within the application. Each picture is loaded into a user control, and placed in a stackpanel, so that when the user clicks the picture, it triggers the code in the user control, and automatically sets their profile picture without any more clicking needed. This window loads on 64-bit systems just fine. However, when loading on a 32-bit system, the entire application crashes. The faulting module is wpfgfx_v0400.dll. I don't know why it's crashing. Please help.
Here's the error in the Event Viewer:
Here's the XAML on the frontend of the window in question:
<Window x:Class="RandomApplication.Windows.ChooseProfilePic"
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:local="clr-namespace:RandomApplication.Windows"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
ContentRendered ="On_ContentRendered"
Title="Choose A Picture" Height="515" Width="500" Background="Black" ResizeMode="CanMinimize">
<Grid Background="Black">
<ScrollViewer VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" Height="420" VerticalAlignment="Top" Margin="0,5,0,0">
<StackPanel>
<StackPanel Name="HeadsPanel" Orientation="Horizontal" Height="100" VerticalAlignment="Top"/>
<StackPanel Name="AbstractPanel" Orientation="Horizontal" Height="100" VerticalAlignment="Top"/>
<StackPanel Name="ShapesPanel" Orientation="Horizontal" Height="100" VerticalAlignment="Top"/>
<StackPanel Name="MiscPanel" Orientation="Horizontal" Height="100" VerticalAlignment="Top"/>
</StackPanel>
</ScrollViewer>
<Button Name="CancelButton" VerticalAlignment="Bottom" Style="{DynamicResource RedButton}" Click="CancelButton_Click">Cancel</Button>
<Border Name="LoadingBorder" Background="Black">
<TextBlock Name="LoadingLabel" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,150" FontSize="20" FontWeight="Bold">
<Run>Loading Pictures</Run>
<LineBreak></LineBreak>
<Run>Please Wait...</Run>
</TextBlock>
</Border>
</Grid>
Here's the code behind the window:
namespace RandomApplication.Windows
{
public partial class ChooseProfilePic : Window
{
private readonly BackgroundWorker _loadPictureWorker = new BackgroundWorker();
public ChooseProfilePic()
{
InitializeComponent();
Topmost = true;
_loadPictureWorker.DoWork += LoadImages;
_loadPictureWorker.RunWorkerCompleted += LoadImages_Completed;
}
private void On_ContentRendered(object sender, EventArgs e)
{
_loadPictureWorker.RunWorkerAsync();
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void LoadImages(object sender, DoWorkEventArgs e)
{
try
{
var headsImagePath = AppDomain.CurrentDomain.BaseDirectory + #"Images\Profile Pics\Heads\";
var abstractImagePath = AppDomain.CurrentDomain.BaseDirectory + #"Images\Profile Pics\Abstract\";
var shapesImagePath = AppDomain.CurrentDomain.BaseDirectory + #"Images\Profile Pics\Shapes\";
var miscImagePath = AppDomain.CurrentDomain.BaseDirectory + #"Images\Profile Pics\Misc\";
List<string> headsImageList = GetImages(headsImagePath);
List<string> abstractImageList = GetImages(abstractImagePath);
List<string> shapesImageList = GetImages(shapesImagePath);
List<string> miscImageList = GetImages(miscImagePath);
Application.Current.Dispatcher.Invoke(() =>
{
LoadViewingPanel(headsImageList, HeadsPanel);
LoadViewingPanel(abstractImageList, AbstractPanel);
LoadViewingPanel(shapesImageList, ShapesPanel);
LoadViewingPanel(miscImageList, MiscPanel);
});
}
catch (Exception ex)
{
CustomMessageBox.Show("Could not load images. :-(", "Image Retrieval Failed", MessageBoxButton.OK,
MessageBoxImage.Error);
Helper.WriteException(Helper.ErrorLogs + "Error Loading Images.txt", ex);
}
}
private void LoadImages_Completed(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
CustomMessageBox.Show("Could not load images. :-(", "Image Retrieval Failed", MessageBoxButton.OK,
MessageBoxImage.Error);
Helper.WriteException(Helper.ErrorLogs + "Error Loading Images.txt", e.Error);
}
else LoadingBorder.Visibility = Visibility.Hidden;
}
public List<string> GetImages(string imagePath)
{
var that = GetAllFiles(imagePath);
return that.ToList();
}
private void LoadViewingPanel(List<string> list, StackPanel panel)
{
foreach (var imageString in list)
{
Helper.WriteLineToFile(Helper.ErrorLogs + "2nd Info Loading Images.txt", imageString);
var thisUri = new Uri(imageString, UriKind.RelativeOrAbsolute);
var pic = new ProfilePic {ProfilePicImage = {Source = new BitmapImage(thisUri)}};
panel.Children.Add(pic);
}
}
private IEnumerable<string> GetAllFiles(string path)
{
return Directory.EnumerateFiles(path, "*.jpg").Union(
Directory.EnumerateDirectories(path).SelectMany(d =>
{
try
{
return GetAllFiles(d);
}
catch
{
return Enumerable.Empty<string>();
}
}));
}
}
}
I've researched what could cause issues with this particular dll, but none of it seems to relate to my issue.
So, I figured out the issue. Apparently, the size of the images that I was trying to load was too big. The images were all 2048x2048 pixels, which made them anywhere from 180 KB to 380 KB in size. Apparently this is too much. I resized all of the pictures to 100x100 pixels (as I was only ever presenting them to the user as 100x100), which brought the file sizes down to 7 - 10 KB each. After that, they loaded just fine with no crashing issues.

How can I WPF bind dynamically created checkboxes to dynamically created series in LiveCharts?

I have a liveChart and am creating checkboxes for each item in a list. This list also has data for each series in liveCharts. How do I bind my dynamically created checkboxes with each individual LiveCharts.LineSeries from my data?
I've created the checkboxes:
<!-- Creating checkboxes by binding to list -->
<ListView ItemsSource="{Binding ElementItemList}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" Width="600">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ElementName}" />
<CheckBox IsChecked="{Binding Path=ElementIsSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>
<!-- Display the chart -->
<Grid Grid.Row="1" x:Name="TestGrid"></Grid>
So I assume that you want to have a CheckBox representing each LineSeries in your SeriesCollection.
So I would have two public properties, one for the SeriesCollection and the other for the list of CheckBox controls.
public SeriesCollection SeriesCollection { get; set; }
public List<CheckBox> CheckBoxCollection { get; set; }
Then following is a function that mimics dynamically creating your LineSeries and CheckBox controls since you didn't provide that code. It is important to have some sort of a connection between the CheckBox controls and your line series, and in this case I decided to set LineSeries.Title and CheckBox.Name the same.
Also note that in order to have the CheckBox do something upon checking/unchecking, you'd need to register two events for each.
public void DynamicallyCreateStuff()
{
SeriesCollection = new SeriesCollection();
CheckBoxCollection = new List<CheckBox>();
var count = 3;
var val1 = new List<double>() { 1, 2, 3 };
var val2 = new List<double>() { 9, 5, 3 };
var val3 = new List<double>() { 1, 4, 9 };
for (int i = 1; i <= count; i++)
{
var name = string.Format("LineSeries{0}", i);
var checkBox = new CheckBox
{
Name = name,
Content = name,
Margin = new Thickness() { Left = 8, Top = 8, Right = 8, Bottom = 8 },
IsChecked = true
};
checkBox.Checked += DynamicCheckBoxChecked;
checkBox.Unchecked += DynamicCheckBoxUnchecked;
CheckBoxCollection.Add(checkBox);
var lineSeries = new LineSeries
{
Title = name
};
if (i == 1)
{
lineSeries.Values = new ChartValues<double>(val1);
}
else if (i == 2)
{
lineSeries.Values = new ChartValues<double>(val2);
}
else if (i == 3)
{
lineSeries.Values = new ChartValues<double>(val3);
}
SeriesCollection.Add(lineSeries);
}
}
In my case, I decided to have the corresponding series become visible/hidden upon clicking the CheckBox, so my check/uncheck methods look like this:
private void DynamicCheckBoxChecked(object sender, EventArgs e)
{
ShowHideSeries(sender, Visibility.Visible);
}
private void DynamicCheckBoxUnchecked(object sender, EventArgs e)
{
ShowHideSeries(sender, Visibility.Collapsed);
}
private void ShowHideSeries(object sender, Visibility visibility)
{
var checkBox = (CheckBox)sender;
var found = SeriesCollection.FirstOrDefault(x => x.Title == checkBox.Name);
if (found != null)
{
var series = (LineSeries)found;
series.Visibility = visibility;
}
}
I didn't use a ViewModel in order to save time and for the sake of simplicity, so my MainWindow constructor looks like this:
public MainWindow()
{
InitializeComponent();
DynamicallyCreateStuff();
DataContext = this;
}
And XAML is pretty bare bones here:
<Window x:Class="SOLineCharts.MainWindow"
....
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0"
ItemsSource="{Binding CheckBoxCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<lvc:CartesianChart Series="{Binding SeriesCollection}" Grid.Column="1"/>
</Grid>
</Window>
Result:
Upon loading:
Unchecking one check box:

Showing different message in multilanguage dynamically

I have successfully create a multilanguage application based from here. I was great when able to change language.
Now I have stuck in this situation. My application is operating with hardware. So there is one screen that having interaction with hardware and display status textblock. The message will be variant depend on the response from hardware e.g. "Please wait..", "Scan your ID into scanner", "Scan complete", "Profile identified, continue with transaction".
How do this variant can be display in multilingual into single textblock?
Assuming the textblock will be naming TbxStatus.Text. How do I set the message in ResourceDictionary file and how do I handle which resource string key that it should take?
EDITED [WHAT HAVE I TRIED]
This is the code that I've write to switch language and show based from resources dictionary:-
App.cs
public static String Directory;
public static App Instance;
public static event EventHandler LanguageChangedEvent;
public App()
{
// Initialize static variables
Instance = this;
Directory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Instance.SetLanguageResourceDictionary(Instance.GetLocXAMLFilePath("en-US"));
}
public static void LoadLanguageLocalization()
{
try
{
ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc = new List<ApplicationModel.LanguageLocalization>
{
new ApplicationModel.LanguageLocalization { LanguageID = 1, CountryCode = "ms-MY", LanguageName = "Bahasa Malaysia" },
new ApplicationModel.LanguageLocalization { LanguageID = 2, CountryCode = "en-US", LanguageName = "English" },
new ApplicationModel.LanguageLocalization { LanguageID = 3, CountryCode = "zh-CN", LanguageName = "Chinese" },
new ApplicationModel.LanguageLocalization { LanguageID = 4, CountryCode = "ta-IN", LanguageName = "Tamil" }
};
}
catch (Exception ex)
{
LogEvents($"[App] Exception on LoadLanguageLocalization. Message-{ex.Message}. Stack Trace-{ex.StackTrace}", EventLogEntryType.Error);
ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc = null;
}
}
public void SwitchLanguage(string inFiveCharLang)
{
if (System.Globalization.CultureInfo.CurrentCulture.Name.Equals(inFiveCharLang))
return;
var ci = new System.Globalization.CultureInfo(inFiveCharLang);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
SetLanguageResourceDictionary(GetLocXAMLFilePath(inFiveCharLang));
LanguageChangedEvent?.Invoke(this, new EventArgs());
}
private string GetLocXAMLFilePath(string inFiveCharLang)
{
string locXamlFile = "Resources." + inFiveCharLang + ".xaml";
return Path.Combine(Directory, "Language", locXamlFile);
}
public void SetLanguageResourceDictionary(String inFile)
{
if (File.Exists(inFile))
{
// Read in ResourceDictionary File
var languageDictionary = new ResourceDictionary();
languageDictionary.Source = new Uri(inFile);
// Remove any previous Localization dictionaries loaded
int langDictId = -1;
for (int i = 0; i < Resources.MergedDictionaries.Count; i++)
{
var md = Resources.MergedDictionaries[i];
// Make sure your Localization ResourceDictionarys have the ResourceDictionaryName
// key and that it is set to a value starting with "Loc-".
if (md.Contains("LanguageDictionaryName"))
{
if (md["LanguageDictionaryName"].ToString().StartsWith("Lang-"))
{
langDictId = i;
break;
}
}
}
if (langDictId == -1)
{
// Add in newly loaded Resource Dictionary
Resources.MergedDictionaries.Add(languageDictionary);
}
else
{
// Replace the current langage dictionary with the new one
Resources.MergedDictionaries[langDictId] = languageDictionary;
}
}
}
SelectLanguage.cs
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
try
{
App.LogEvents($"[{PageTitle}] Loaded: Select language", System.Diagnostics.EventLogEntryType.Information);
BindingToPropertyControl();
}
catch (System.Exception ex)
{
string error = $"[{PageTitle}] Exception on Page_Loaded. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
App.LogEvents(error, System.Diagnostics.EventLogEntryType.Error);
}
}
private void BindingToPropertyControl()
{
try
{
if (ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc != null)
{
LanguagePack.ItemsSource = ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc;
}
}
catch (System.Exception ex)
{
string error = $"[{PageTitle}] Exception on BindingToPropertyControl. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
App.LogEvents(error, System.Diagnostics.EventLogEntryType.Error);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
ScreenTimer.Stop();
Button btn = (Button)sender;
string LangCode = btn.Tag.ToString();
App.LogEvents($"[{PageTitle}] Selecting language: {LangCode}", System.Diagnostics.EventLogEntryType.Information);
App.Instance.SwitchLanguage(LangCode.ToString());
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(delegate ()
{
NavigationService.Navigate(new Uri(ApplicationModel.NaviModel.NaviSelectOptions, UriKind.RelativeOrAbsolute));
}));
}
catch (System.Exception ex)
{
string error = $"[{PageTitle}] Exception on Button_Click. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
App.LogEvents(error, System.Diagnostics.EventLogEntryType.Error);
}
}
SelectLanguage.xaml
<ScrollViewer x:Name="ScrollLanguage" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
<WrapPanel Height="Auto" Width="{Binding ElementName=ScrollLanguage, Path=ViewportWidth}">
<ItemsControl Name="LanguagePack">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,20" VerticalAlignment="Stretch" Width="{Binding ElementName=ScrollLanguage, Path=ViewportWidth}">
<Button Click="Button_Click" Tag="{Binding CountryCode}" Content="{Binding LanguageName}" VerticalAlignment="Center" Height="150" FontSize="60" Background="#FF1A5C9E" BorderBrush="{x:Null}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</WrapPanel>
</ScrollViewer>
SelectOptions.xaml
<TextBlock x:Name="tbTitle" TextWrapping="Wrap" Text="{StaticResource ResourceKey=SelectMerchant_Title}" FontSize="100" TextAlignment="Center" Padding="0,0,0,50" Foreground="White"/>
<Button x:Name="btnEatIn" Content="{StaticResource ResourceKey=SelectMerchant_Opt1}" VerticalAlignment="Center" Height="150" FontSize="60" Background="#FF057A5A" BorderBrush="{x:Null}"/>
<Button x:Name="btnEatIn" Content="{StaticResource ResourceKey=SelectMerchant_Opt2}" VerticalAlignment="Center" Height="150" FontSize="60" Background="#FF057A5A" BorderBrush="{x:Null}"/>
Resources.en-US.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<!-- The name of this ResourceDictionary. Should not be localized. -->
<sys:String x:Key="LanguageDictionaryName" Localization.Comments="$Content(DoNotLocalize)">Lang-en-US</sys:String>
<!-- Localization specific styles -->
<FlowDirection x:Key="FlowDirection_Default" Localization.Comments="$Content(DoNotLocalize)">LeftToRight</FlowDirection>
<!--<FlowDirection x:Key="FlowDirection_Reverse" Localization.Comments="$Content(DoNotLocalize)">RightToLeft</FlowDirection>-->
<!-- SELECT ORDER TYPE -->
<sys:String x:Key="SelectMerchant_Title">Self-Service Kiosk</sys:String>
<sys:String x:Key="SelectMerchant_Opt1">Register new applicant</sys:String>
<sys:String x:Key="SelectMerchant_Opt2">Meal Application</sys:String>
</ResourceDictionary>
Back to what I'm facing, I can show different language by using resource key, but how to I display message or status which is dynamically (not static) into the display in multi-language?
Example, on validation screen, I have one TextBlock and currently I'm subscribe the event raise from hardware. How to show the status based from language that has been selected?
.
<StackPanel VerticalAlignment="Top" Margin="120,180,120,0" Grid.Row="1">
<TextBlock x:Name="tbGeneralStatus" TextWrapping="Wrap" Text="Please wait..." TextAlignment="Center" FontSize="50" Foreground="Yellow"/>
</StackPanel>
tbGeneralStatus.Text will show "Please wait..", "Scan your ID into scanner", "Scan complete", "Profile identified, continue with transaction" from delegate event from Barcode Scanner class.
I think you need to look a little into MVVM to make things easier with WPF. It takes some effort at the beggining, but its absolutely worth. I thought you were stucked only on how to receive and translate the status, so I'll try to give more info after looking at the code.
A quick guidance, based on Enum localization but not verified in dept.
You need a viewmodel to act as datacontext of the window you want to update. It has to implement INotifyPropertyChange interface to update the status in live.
class YourWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _statusText = string.Empty;
public string StatusText
{
get { return _statusText; }
set
{
_statusText = value;
OnPropertyChanged("StatusText");
}
}
public void YourMessageHandler(Status newStatus)
{
StatusText = GetLocalizedStatusText(newStatus);
}
private string GetLocalizedStatusText(Status newStatus)
{
switch (newStatus)
{
case Status.Wait: return Resources.StatusWaiting;
case Status.Continue: return Resources.StatusContinue;
case Status.Scan: return Resources.StatusScanId;
default: return string.Empty;
}
}
}
public enum Status
{
Wait,
Scan,
Continue
}
To bind to your window, make it like this:
<Window.DataContext>
<local:YourWindowViewModel/>
</Window.DataContext>
and change your TextBlock control to bind to the StatusText on the viewmodel
<StackPanel VerticalAlignment="Top" Margin="120,180,120,0" Grid.Row="1">
<TextBlock TextWrapping="Wrap" Text="{Binding StatusText}" TextAlignment="Center" FontSize="50" Foreground="Yellow"/>
</StackPanel>
Note that as I don't know your delegate/msgHandler format, I have put a generic "YourMessageHandler" method which receive the changing status

How to validate RichTextbox in WPF?

How to validate RichTextbox in WPF? I want to validate the text for email and email separator i.e. emails should be entered with a semicolon.
Xaml:
<StackPanel Orientation="Horizontal">
<RichTextBox x:Name="txtEmail" Style="{StaticResource ContentRichTextBox}"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,10,0">
<FlowDocument>
<Paragraph LineHeight="5"></Paragraph>
</FlowDocument >
</RichTextBox>
</StackPanel>
<StackPanel HorizontalAlignment="Center">
<TextBlock x:Name="txterrormessage" Width="300" Foreground="#FFE5572C" FontSize="14" Visibility="Hidden" TextWrapping="Wrap"></TextBlock>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="60,0,0,0">
<Button x:Name="BtnEmail" Style="{StaticResource ShortButtonStyle}" Content="NEXT" Margin="10" Command="{Binding CommandChanged}" CommandParameter="PROJECTS" Click="BtnEmail_Click"/>
</StackPanel>
This is my code:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
if (!Regex.IsMatch(richText, #"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$"))
{
txterrormessage.Text = "Enter a valid email";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
else
{
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
}
if (!Regex.IsMatch(richText, #"^((\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)\s*[;]{0,1}\s*)+$"))
{
txterrormessage.Text = "Separate emails with ;";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
The code doesnt seem to work....How to validate?
Thanks
The simplest way I've seen to do this is
private void OnVerifyEmail()
{
var recipients = richText.Split(';', StringSplitOptions.RemoveEmptyEntries);
var validator = new System.ComponentModel.DataAnnotations.EmailAddressAttribute();
foreach (var recipient in recipients)
{
var isValid = validator.IsValid(recipient.Trim());
if(!isValid)
{
// do your thing here
}
}
}
format your richText before validation:
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
Edit:
This is the whole method and probably what you're looking for:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
richText = Regex.Replace(richText, #"( ;|; )", ";", RegexOptions.Multiline);
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
if (!Regex.IsMatch(richText, #"^[\W]*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4}[\W]*,{1}[\W]*)*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4})[\W]*$"))
{
string[] emails = Regex.Split(richText, ";", RegexOptions.Multiline);
foreach (string item in emails)
{
if (string.IsNullOrEmpty(item))
continue;
if (!Regex.IsMatch(item, #"^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$"))
{
txterrormessage.Text = item + " is not a valid email address";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
break;
}
}
if (string.IsNullOrEmpty(txterrormessage.Text))
{
txterrormessage.Text = "Separate emails with ; ";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
}

Resources