Reasoning:
I've seen this question duplicated a few times on this website, and each one seems to bring people to same conclusion.
Okay, so I have a music player located within a window. When the window opens, an OpenFileDialog method is ran, to where the user must pick a .mp3 file. Once done, they can play the file whilst navigating other windows in the program.
I'm trying to:
Enter the file name of the selected file into a label. With this, I want to show the file name as it is being played. I want to only show the file name, not the path to it as well.
What I've seen on duplicate questions:
Repeating fixes whereby I can use the following code to pick up the file name with or without the file extension:
label1.Content = Path.GetFileName(media.Source.ToString());
label1.Content = Path.GetFileNameWithoutExtension(media.Source.ToString());
Issue:
I've tried to enter this on my program, but I don't have the "Path" section (I'm not too sure of the actual name of this part of code).
The closest I can get to this is: class System.Windows.Shapes.Path | Draws a series of connected lines and curves.
I don't seem to have another "path" in my VS, so I'm not too sure what to do.
Code (Sorry about the code here, it was a little awkward to copy into the question box)
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.Shapes;
using Microsoft.Win32;
using System.Windows.Threading;
namespace WolfAlbumCatalogue
{
/// <summary>
/// Interaction logic for MusicPlayer.xaml
/// </summary>
public partial class MusicPlayer : Window
{
private MediaPlayer mediaPlayer = new MediaPlayer();
AlbumCatalogue albumCatalogue;
public MusicPlayer(AlbumCatalogue parent)
{
InitializeComponent();
albumCatalogue = parent;
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "MP3 files (*.mp3)|*.mp3|All files(*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
mediaPlayer.Open(new Uri(openFileDialog.FileName));
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Tick += miniTimer_Tick;
timer.Start();
albumCatalogue.img_musicPlayerWindow.IsEnabled = false;
lbl_songName.Content = Path
}
private void rec_closeHover_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.Close();
albumCatalogue.img_musicPlayerWindow.IsEnabled = true;
}
void timer_Tick(object sender, EventArgs e)
{
if (mediaPlayer.Source != null)
{
lbl_timer.Content = String.Format("{0} / {1}", mediaPlayer.Position.ToString(#"mm\:ss"), mediaPlayer.NaturalDuration.TimeSpan.ToString(#"mm\:ss"));
}
else
{
lbl_timer.Content = "No file selected...";
}
}
void miniTimer_Tick(object sender, EventArgs e)
{
if (mediaPlayer.Source != null)
{
lbl_miniTimer.Content = String.Format("{0} / {1}", mediaPlayer.Position.ToString(#"mm\:ss"), mediaPlayer.NaturalDuration.TimeSpan.ToString(#"mm\:ss"));
}
else
{
lbl_miniTimer.Content = "No file selected...";
}
}
private void btn_play_Click(object sender, RoutedEventArgs e)
{
mediaPlayer.Play();
lbl_play_pause.Content = "Playing...";
lbl_play_pause.Visibility = Visibility.Visible;
}
private void btn_pause_Click(object sender, RoutedEventArgs e)
{
mediaPlayer.Pause();
lbl_play_pause.Content = "Paused.";
lbl_play_pause.Visibility = Visibility.Visible;
}
private void btn_stop_Click(object sender, RoutedEventArgs e)
{
mediaPlayer.Stop();
lbl_play_pause.Visibility = Visibility.Hidden;
}
private void rec_titleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
private void rec_windowBkg_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
private void rec_btnOpenAudioFile_MouseDown(object sender, MouseButtonEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "MP3 files (*.mp3)|*.mp3|All files(*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
mediaPlayer.Open(new Uri(openFileDialog.FileName));
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
}
}
I would copy in the XAML too but I use blend animations and it's way too long for this question. If you need it, please let me know :)
That said, I think that's everything!
Thank you
Include the header System.IO in your application.
(using System.IO;) .If it is not found you need to add the reference System dll.
Related
It's been 8 hours since i'm stuck here. I want ask user to input a page number and starting from the given page it should add page numbering till the .docx file.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using Word = Microsoft.Office.Interop.Word;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private string fileName;
public Form1()
{
InitializeComponent();
}
private void minimize_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
private void Close_Click(object sender, EventArgs e)
{
this.Close();
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Word Documents|*.docx";
dlg.ShowDialog();
fileName = dlg.FileName;
textBox1.Text = fileName;
textBox1.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
}
}
}
On button2_click i want to update the file by using data from a textBox2, can anybody complete it?
I won't complete it, but I will point you in the right direction. You have to have a reference to Microsoft Word. What you are trying to do is typically referred to as automation or more specifically Microsoft Word Automation.
You can google that and find examples or try this location for starting point.
https://learn.microsoft.com/en-us/previous-versions/office/troubleshoot/office-developer/automate-word-create-file-using-visual-c
I want to achieve almost something similar here. I want a control that supports both images or video. This will play for some duration.
I am able to show the video using WMP(Windows media player) object. But now I am trying to use DirectX to render the images or video.
Please help me on this. Suggestion are welcomed.
Here is what I tried.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.DirectX.AudioVideoPlayback;
using Microsoft.DirectX;
namespace WindowsPlayerDirectX
{
public partial class Form1 : Form
{
private Video video;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
//int height = panel1.Height;
//int width = panel1.Width;
try
{
MessageBox.Show("Hi", "Test");
openFileDialog1.Filter = "Image Files(jpg,png,bmp,gif)|*.jpg;*.jpeg;*.png;*.bmp;*.gif|all files|*.*";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// video = new Video(openFileDialog1.FileName,false);
pictureBox1.Load(openFileDialog1.FileName);
}
}catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
//video = new Video("D:\\Wildlife.wmv", false);
//video.Owner = panel1;
//video.Stop();
//video.Dispose();
//if (!video.Playing)
// video.Play();
//else
// MessageBox.Show("File already playing D:\\Wildlife.wmv ", "Info");
//OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog1.ShowDialog();
openFileDialog1.Title = "Select video file..";
openFileDialog1.InitialDirectory = Application.StartupPath;
openFileDialog1.DefaultExt = ".avi";
openFileDialog1.Filter = "Media Files|*.mpg;*.avi;*.wma;*.mov;*.wav;*.mp2;*.mp3|All Files|*.*";
video = new Video(openFileDialog1.FileName);
video.Owner = panel1;
panel1.Width = 700;
panel1.Height = 390;
}
}}
I'm not by any means an expert in DirectX applications, but I've made one with OpenGL...
I guess what's missing is some kind of Surface to render the video into.
BTW, it seems that Microsoft DirectX in .Net is deprecated. I guess that you'll be better off using SharpDx.
you forgot to call video.Play();
or you can add autopaly in constructor like this:
video = new Video(openFileDialog1.FileName, true);
I used Accord.net (AForge) for connect to the webcam and record video
But stored videos is slow motion ...
this my project :
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.FFMPEG;
using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace CaptureWebcam
{
public partial class Form1 : Form
{
private VideoCaptureDeviceForm captureDevice;
private string path = Path.GetDirectoryName(Application.ExecutablePath) + #"\Videos\";
private FilterInfoCollection videoDevice;
private VideoCaptureDevice videoSource;
private VideoFileWriter FileWriter = new VideoFileWriter();
private Bitmap video;
bool isRecord = false;
public Form1()
{
InitializeComponent();
}
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
video = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void btnStartCam_Click(object sender, EventArgs e)
{
videoDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
videoSource = captureDevice.VideoDevice;
videoSource = captureDevice.VideoDevice;
videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
videoSource.Start();
timer1.Enabled = true;
}
//videoSource.DisplayPropertyPage(IntPtr.Zero);
}
private Thread workerThread = null;
private bool stopProcess = false;
private void recordLiveCam()
{
if (!stopProcess)
{
while (isRecord)
{
FileWriter.WriteVideoFrame(video);
}
FileWriter.Close();
}
else
{
workerThread.Abort();
}
}
private void btnRecord_Click(object sender, EventArgs e)
{
//try
//{
isRecord = true;
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open(path + "recorded at " + DateTime.Now.ToString("HH-mm-ss") + ".mp4", w, h, 25, VideoCodec.MPEG4);
stopProcess = false;
workerThread = new Thread(new ThreadStart(recordLiveCam));
workerThread.Start();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.Message);
//}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnStopRecord_Click(object sender, EventArgs e)
{
stopProcess = true;
isRecord = false;
FileWriter.Close();
workerThread.Abort();
video = null;
}
private void btnStopCam_Click(object sender, EventArgs e)
{
try
{
if (videoSource.IsRunning)
{
videoSource.SignalToStop();
videoSource.Stop();
pictureBox1.Image = null;
pictureBox1.Invalidate();
if (FileWriter.IsOpen)
{
FileWriter.Close();
video = null;
}
}
else
return;
}
catch
{
videoSource.Stop();
FileWriter.Close();
video = null;
}
}
float fts = 0.0f;
private void timer1_Tick(object sender, EventArgs e)
{
fts = videoSource.FramesReceived;
label1.Text = "Frame Rate : " + fts.ToString("F2") + " fps";
}
}
}
And when click the btnStopRecord following error :
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
"Slow motion" can have multiple reasons. First you need to know if the actual framerate, at which the NewFrames get produces is so slow (how many HZ then?) or if the CPU is too busy. Or the graphics card maybe.
For everything DirectShow related I strongly suggest using GraphEdit and AMCap to see what the real capabilites of the device are. AForge was prone to oversampling and I would not wonder if it's similar in Accord.
Also, you can always rely on good old processexplorer to see if the load (if any) is caused by SYSTEM or INTERRUPRS or is actually produced by your executable.
Another thing that happened to me was PictureBox. VideoSourcePlayer is far better and I wound up doing my own blitting.
It is worth mentioning these three optimizations:
DO NOT USE Bitmap.GetPixel!
Use videoSource.DisplayPinPropertyPage(IntPtr.Zero)
Check the color space of the videostream
Bitmap.GetPixel
The problem with GetPixel is, that it's really slow as it has to unmanage/manage a lot for every single pixel. It is fine as along as you only get a small a mount of calls and use it on a loaded bitmap. It is definately the wrong approach if you want to use it on all pixelx every couple of milliseconds. The CPU load from this phenomenon is associated with your application and will show as such in process explorer.
In the end I wrote my own pixel routines starting here: Work with bitmap faster with Csharp
If you just want a kernel or something more usual than I needed, implement a
custom Filter.
VideoSource.DisplayPinPropertyPage
If you use the AForge-buit-in mechanism for choosing the resolution and initiating the video stream, you cannot set the Framerate (I do not consider this a bug in AForge). So AForge always chooses the highest framerate. If you circumvent videoSource.VideoCapabilities and directly show the native device configuration dialog (it's formally called "Video Capture Pin Properties Dialog"). There you can set all the parameters manually and the dialog will fill in the appropriate framerate. This way, you will get the "real" (hardware) refresh rate in your callbacks. The CPU load of this oversampling happens in the SYSTEM process.
Color space conversion
In my case, the camera supports three output formats: YUY2, MJPG and RGB24. Depending on what format you use, the image is transformed by AForge into RGB (I think actually it's ARGB32). The rendered DirectShow graphs for all three formats are different and RGB obviously uses much less CPU than the others, as the color conversion is quite trivial. The load from this conversion also shows up in the SYSTEM process and not in your applications exe.
You can try to put some timers to understand which operation is slowing down the process.(Stopwatch class will be perfect)
I don't know the format of your Frame but usually the conversion to Bitmap is the bottle neck, AForge is quite fast. Furthermore you can pass a time stamp to WriteVideoFrame method, then the frame rate indicated in Open is just for replaying the video. Aforge will ordinate the frames in the right order with the right time.
That's according to my experience. Hope it can help.
Try this
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AForge;
using AForge.Video.DirectShow;
using Accord.Video.FFMPEG;
using System.IO;
using AForge.Video;
using System.Threading;
namespace New_Project_2
{
public partial class Form1 : Form
{
private VideoCaptureDeviceForm captureDevice;
private FilterInfoCollection videoDevice;
private VideoCaptureDevice videoSource;
private VideoFileWriter FileWriter = new VideoFileWriter();
bool isRecord = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (isRecord == true)
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
FileWriter.WriteVideoFrame((Bitmap)eventArgs.Frame.Clone());
}
}
catch (Exception)
{
throw;
}
}
private void button1_Click(object sender, EventArgs e)
{
videoDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
isRecord = true;
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open("d:\\" + "recorded at " + DateTime.Now.ToString("HH-mm-ss") + ".mp4", w, h, 25, VideoCodec.MPEG4);
videoSource = captureDevice.VideoDevice;
videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
videoSource.Start();
}
//videoSource.DisplayPropertyPage(IntPtr.Zero)
}
private void button2_Click(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
isRecord = false;
FileWriter.Close();
}
private void button4_Click(object sender, EventArgs e)
{
}
}
}
You can use emguCV to record videos.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu;
using Emgu.CV;
using Emgu.CV.Structure;
namespace Load_Images
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
double TotalFrame;
double Fps;
int captureFrameNo;
VideoCapture capture;
VideoWriter writer;
bool isCapturing = false;
private void button1_Click(object sender, EventArgs e)
{
if (isCapturing == false)
{
capture = new VideoCapture(0);
TotalFrame = capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameCount);
Fps = capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps);
isCapturing = true;
int fourcc = fourcc = VideoWriter.Fourcc('H', '2', '6', '4');
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 2048);
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 1024);
//int fourcc = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FourCC));
int width = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth));
int height = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight));
string destin_path = "D:\\out.mp4";
writer = new VideoWriter(destin_path, fourcc, Fps, new Size(width, height), true);
capture.ImageGrabbed += Capture_ImageGrabbed;
capture.Start();
}
}
private void Capture_ImageGrabbed(object sender, EventArgs e)
{
try
{
if (isCapturing == true)
{
if (capture == null)
{
return;
}
Mat m = new Mat();
capture.Retrieve(m);
writer.Write(m);
pictureBox1.Image = m.ToImage<Bgr, byte>().Bitmap;
}
}
catch (Exception)
{
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (isCapturing == true) {
capture.Stop();
isCapturing = false;
}
}
private void button3_Click(object sender, EventArgs e)
{
if (capture != null) {
capture.Pause();
}
}
private void button4_Click(object sender, EventArgs e)
{
if (writer.IsOpened)
{
isCapturing = false;
writer.Dispose();
}
MessageBox.Show("Completed");
}
private void Form2_Load(object sender, EventArgs e)
{
}
}
}
if you write file on newFrame handler Func will cause fastplaqy/slow record on some codec VP8-VP9 etc. (mpeg4 not effect) but if you start different timer on some time to write you solve your issues
I have simple WPF form in .NET, which allows mw to select directory and then perform some action on files in them
I have button, which triggers the action (it is transforming XML files to csv). This takes some time, so I would like to write into textbox information about the number of files processed etc.
I can do that, but all messages which are send to textbox during Click action are shown after the whole procedure is finished. What I want, is to send messages to textbox while processing the data in Click method.
This is method triggered on click:
private void processButton_Click(object sender, RoutedEventArgs e)
{
List<string> allXmlFiles = ProcessDirectory(selectedDir);
textbox.Text += String.Format("\n{0} files will be processed", allXmlFiles.Count);
if (allXmlFiles.Count > 0)
{
textbox.Text += "\nProcessing files...";
foreach (string filepath in allXmlFiles)
{
try
{
ParseFile(filepath);
}
catch
{
textbox.Text += String.Format("\nCannot process file {0}", filepath);
}
}
}
textbox.Text += "\nDone";
}
How can I make the messages ("X files will be processed", "Processing files...", "Cannot process file XYZ") appear while calculating and not after?
this because you are starting your parsing synchronously to overcome your issue try BackgroundWorker with something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 System.ComponentModel;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
BackgroundWorker bckg = new BackgroundWorker();
private List<string> allXmlFiles;
public MainWindow()
{
InitializeComponent();
bckg.DoWork += new DoWorkEventHandler(bckg_DoWork);
bckg.ProgressChanged += new ProgressChangedEventHandler(bckg_ProgressChanged);
bckg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bckg_RunWorkerCompleted);
}
void bckg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error!=null)
{
textbox.Text += String.Format("\nCannot process file {0}", filepath);
}
}
void bckg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//here you can update your textblox
Dispatcher.Invoke(() =>
{
textbox.Text = e.UserState.ToString();
});
}
void bckg_DoWork(object sender, DoWorkEventArgs e)
{
allXmlFiles = ProcessDirectory(selectedDir);
if (allXmlFiles.Count > 0)
{
bckg.ReportProgress("here in percentage", "\nProcessing files...");
foreach (string filepath in allXmlFiles)
{
try
{
ParseFile(filepath);
}
catch
{
throw;
}
}
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
textbox.Text += String.Format("\n{0} files will be processed", allXmlFiles.Count);
bckg.RunWorkerAsync();
}
}
}
This is happening, because your operations are synchronous. Extract your processing files operation in another thread. If you are using .NET 4.5+, try adding the async word in the method:
private async void processButton_Click(object sender, RoutedEventArgs e)
You could have your 'Click' event call another method which handles this logic to end this method as soon as possible.
Otherwise a simple work around to what you already have would be to have a method to update your log information.
For example:
UpdateText(String newText)
{
textBox.Text += "\r\n" + newText;
}
With this, using your loop, you could report the number of files processed. You could do this in chunks to avoid spam.
Your for loop, with suggestions:
int numProcessed = 0;
foreach (string filepath in allXmlFiles)
{
try
{
ParseFile(filepath);
numProcessed++; // or you could use '++numProcessed' inline
if(numProcessed % 10 == 0 && numProcessed >= 10)
UpdatedText("Another 10 files processed!");
}
catch
{
UpdateText(String.Format("\nCannot process file {0}", filepath));
}
}
UpdatedText("Finished");
I have created a RichTextBox text editor as a c# Form application solution. I have added a WPF application to this solution to act as a spellchecker. Basically, when I run the spell checker the contents of the RichTextBox are copied to a WPF RichTextBox in the WPF application which I have configured to look like a spell check dialog. I have included a ListBox which displays the spelling error suggestions and buttons that allow the user to ignore errors or change if one of the suggestions are clicked.
The main routine loops through the text in the RichTextBox until it finds a spelling error. The user then has the option to ignore by clicking a button that calls EditingCommands.IgnoreSpellingError or change by clicking a button that calls EditingCommands.CorrectSpellingError. Currently I am using another button to then re-cycle through the RichTextBox to find the next spelling error.
Ideally what I would like to happen is, for example, when the ignore button is clicked, the EditingCommands.IgnoreSpellingError is called and then the main routine is run immediately after, like so:
private void button3_Click(object sender, RoutedEventArgs e)
{
button3.Command = EditingCommands.IgnoreSpellingError;
button3.CommandTarget = richTextBox1;
spellCheckWords();
}
However, this does not work as the RichTextBox seems to go out of synchronisation with the spell checker. It's as if the form or the RichTextBox are not refreshing properly. Just to re-iterate, if I use a separate button to re-run the main routine then it works ok.
The full code is below.
Any help would be appreciated.
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 System.IO;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
richTextBox1.SpellCheck.IsEnabled = true;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
richTextBox1.Paste();
richTextBox1.ContextMenu = GetContextMenu();
spellCheckWords();
}
private static int chrPos = 0;
private void spellCheckWords()
{
SpellingError spellingError;
TextRange spellErrorRange;
TextPointer start_pointer, end_pointer;
richTextBox1.ContextMenu = GetContextMenu();
richTextBox1.SelectAll();
int txtLen = richTextBox1.Selection.Text.Length;
bool noSpellingErrors = true; ;
for (int i = chrPos; i < txtLen; i++)
{
start_pointer = richTextBox1.Document.ContentStart.GetNextInsertionPosition
(LogicalDirection.Forward).GetPositionAtOffset(i, LogicalDirection.Forward);
spellingError = richTextBox1.GetSpellingError(start_pointer);
if (spellingError != null)
{
spellErrorRange = richTextBox1.GetSpellingErrorRange(start_pointer);
int errRange = spellErrorRange.Text.Length;
textBox1.Text = spellErrorRange.Text;
noSpellingErrors = true;
string textRun = start_pointer.GetTextInRun(LogicalDirection.Forward);
string trimmedString = string.Empty;
end_pointer = richTextBox1.Document.ContentStart.GetNextInsertionPosition
(LogicalDirection.Forward).GetPositionAtOffset(i + errRange, LogicalDirection.Forward);
richTextBox1.Selection.Select(start_pointer, start_pointer);
richTextBox1.Focus();
Rect screenPos = richTextBox1.Selection.Start.GetCharacterRect(LogicalDirection.Forward);
double offset = screenPos.Top + richTextBox1.VerticalOffset;
richTextBox1.ScrollToVerticalOffset(offset - richTextBox1.ActualHeight / 2);
listBox1.Items.Clear();
foreach (string str in spellingError.Suggestions)
{
listBox1.Items.Add(str);
}
//chrPos = i + errRange + 1;
return;
}
}
if (noSpellingErrors == true)
{
textBox1.Text = "No spelling Errors";
listBox1.Items.Clear();
button2.IsEnabled = false;
button3.IsEnabled = false;
button4.IsEnabled = false;
button6.IsEnabled = false;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
richTextBox1.SelectAll();
richTextBox1.Copy();
richTextBox1.Document.Blocks.Clear();
richTextBox1.SpellCheck.IsEnabled = false;
this.Close();
}
private ContextMenu GetContextMenu()
{
return null;
}
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (listBox1.SelectedItem != null)
textBox1.Text = listBox1.SelectedItem.ToString();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
button2.Command = EditingCommands.CorrectSpellingError;
button2.CommandParameter = textBox1.Text;
button2.CommandTarget = richTextBox1;
spellCheckWords();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
button3.Command = EditingCommands.IgnoreSpellingError;
button3.CommandTarget = richTextBox1;
spellCheckWords();
}
private void button4_Click(object sender, RoutedEventArgs e)
{
spellCheckWords();
}
}
}
After a while i got your problem^^ (i think) and it is easy to fix.
In you Button-Click-events you set the command of the button and the parameters and then execute spellCheckWords. But the actual command is executed AFTER the code has leaved the method.
Which means, spellCheckWords() is executed BEFORE IgnoreSpellingError.
Change your code to this:
private void button2_Click(object sender, RoutedEventArgs e)
{
EditingCommands.CorrectSpellingError.Execute(textBox1.Text, richTextBox1);
spellCheckWords();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
EditingCommands.IgnoreSpellingError.Execute(null, richTextBox1);
spellCheckWords();
}
This will execute the command first and then the spellcheck again.