I have been reading about the best way to open dialogs using PRISM and opening them the MVVM way, however what I have found so far seems to only work with custom dialogs. I'd like to be able to open a CommonOpenFileDialog when a button is clicked but I am very confused how I can do this while adhering to the MVVM pattern.
I can accomplish this using the code behind but I would like to avoid this if possible, but the problem I run into here is how I can then pass the dialog result to the view model from the code behind. Would an EventAggregator be acceptable here?
This is my SelectFolderDialog class which opens the dialog. This is currently called from my views code behind:
public class SelectFolderDialog
{
public string SelectFolder()
{
var folderSelectorDialog = new CommonOpenFileDialog();
folderSelectorDialog.EnsureReadOnly = true;
folderSelectorDialog.IsFolderPicker = true;
folderSelectorDialog.AllowNonFileSystemItems = false;
folderSelectorDialog.Multiselect = false;
folderSelectorDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
folderSelectorDialog.Title = "Select Folder";
folderSelectorDialog.ShowDialog();
string SelectedFolderPath = folderSelectorDialog.FileName;
Console.WriteLine(SelectedFolderPath);
return SelectedFolderPath;
}
}
The most MVVMy way is to have a SystemDialogService that you inject into your VMs, that has a method GetOpenFileDialogPath or something, that does exactly what your code does.
That way you can easily mock it in your unit tests and reuse it in other VMs.
Also, having a code behind IS NOT A BAD THING!
So, you rename your class by adding Service at the end, extract an interface for it and inject it in VM constructor using whatever dependency injection form you use.
Then in your button click command you can do:
var path = _systemDialogService.SelectFolder() - that's all
A few years ago I wrote up a fairly detailed article showing how to do pure MVVM dialogs with data-binding, you might want to check out the sample project. In addition to the common system dialogs it also works fine with 3rd party dialog libraries.
I might be updating it soon actually, as I've recently implemented custom WPF dialog boxes (arbitrary shapes etc) which are similar but have a few quirks to you have to be mindful of.
Related
Right now, I am trying to develop a program using Mono and GTK# on a Debian (Raspbian) system.
The issue I'm facing is, that, completely randomly, the GUI (generated by the Stetic designer or its dynamic elements) isn't completely drawn, missing either a few characters from a Label-element or whole widgets, mostly those that were dynamically created. This is how it looks on a dialog window: http://imgur.com/oEZRg7c (text is cut off)
As soon as one window shows this issue, every other window has the same issues, sometimes missing whole widgets, even if those were created afterwards. The solution is usually to quit the program and reopen it, as it only randomly occurs.
This is how the constructor of most of my windows looks like (the part after Build() varies):
public partial class ErrorSolutionDialog : Gtk.Dialog
{
public ErrorSolutionDialog (string errorMessage, string solutionHint)
{
this.WidthRequest = this.Screen.Width;
this.HeightRequest = this.Screen.Height;
this.Maximize ();
this.Fullscreen ();
this.KeepAbove = true;
this.DestroyWithParent = false;
Build ();
this.ErrorMessage.Markup = "<b><span size='xx-large'>" + errorMessage + "</span></b>";
this.SolutionHint.Text = solutionHint;
}
}
I wouldn't say that the use of the Stetic designer inside Xamarin Studio/Monodevelop is bad, but as any piece of software it certainly has some issues.
Also, the use of any designer in any software environment will tie you to that development platform forever. Finally, the created source code will be hardly maintainable, apart from completely foreign for you.
That's why I always recommend to get rid of the designer. You can follow a Gtk# tutorial such as this one, and you'll find it is easy and rewarding. And you'll have whole and thorough control of your code.
The basics about Gtk# is creating a layout with VBoxes and HBoxes. For example, the following code creates a layout in which you'll have a TreeView and a TextView in a Dialog.
var swWin1 = new Gtk.ScrollWindow();
var swWin2 = new Gtk.ScrollWindow();
// TextView
this.txtView = new Gtk.TextView();
swWin1.AddWithViewport( this.txtView );
// TreeView
this.tvView = new Gtk.TreeView();
swWin2.AddWithViewport( this.tvView );
// Layout
var hBox = new HBox( false, 2 );
hBox.PackStart( swWin1, true, true, 5 );
hBox.PackStart( swWin2, true, true, 5 );
this.VBox.PackStart( hBox, true, true, 5 );
PackStart() is the method doing the magic in order to add a widget to a layout. The booleans tell Gtk to expand the widget. A ScrollWindow adds scrollbars to any widget.
Finally, my advice is for any action, use Gtk.Action, and call its methods CreateMenuItem() and CreateToolItem() in order to create menu entries and toobar buttons, instead of repeating the same code again and again.
Hope this helps.
I'am running across this issue when I'm debugging or running my coded UI automation project, where i get the exception labeled "{"COM object that has been separated from its underlying RCW cannot be used." System.Exception {System.Runtime.InteropServices.InvalidComObjectException}" everytime i come from a browser window that contains a pdf reader embedded in it. This happens every time I retrieve the window and try to click back. It barfs when i perform the back method on it. I've tried different things but none has worked including the playback wait.
var hereIsmypdf = ReturnPDFDoc();
public BrowserWindow ReturnPDFDoc()
{
Playback.Wait(1000);
var myPdFdoc = GlobalVariables.Browser;
return myPdFdoc;
}
hereIsmypdf.Back();
The only way i was able to get around this issue was not to use the BrowserWindow class. I ended up using the WinWindow class and just getting the tab of the window from it. The BrowserWindow class seemed to trigger the exception "COM object that has been separated from its underlying RCW cannot be used." System.Exception {System.Runtime.InteropServices.InvalidComObjectException}" everytime i tried to retrieve it. I hope this helps someone one or maybe someone has a better way to handle this issue.
For the people that voted my question down, i really did try to figure it out. Sorry i wasnt clear about what i was asking the community or couldn't properly articulate what this pain was. I'm sure someone probably is going through the same pain i did and having a hard time articulating whats going on.
Here is my code on what i ended up doing
public WinTabPage ReturnPDFDoc()
{
WinWindow Wnd = new WinWindow();
Wnd.SearchProperties[BrowserWindow.PropertyNames.ClassName] = "IEFrame";
WinTabList tabRoWlist = new WinTabList(Wnd);
tabRoWlist.SearchProperties[WinTabPage.PropertyNames.Name] = "Tab Row";
WinTabPage myTab = new WinTabPage(tabRoWlist);
myTab.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
myTab.SearchProperties[WinTabPage.PropertyNames.Name] = "something";
//UITestControlCollection windows = newWin.FindMatchingControls();
return myTab;
}
Being a neophyte in C#, WPF and MVVM, I am refactoring my first app prototype from a kludge that was almost fully implemented in the code-behind to an MVVM pattern. I have everything working nicely: data binding, commands, etc. Almost everything has been moved to its correct location but I am running into a little bit of an issue trying to figure out how I should handle files.
My model supports a chunk of data that is sent to and read from a remote electronic device. That data is transformed in the VM and exchanged with the V via binding. The user may optionally select to stream output to a CSV. This can be done via an OpenFileDialog or by entering the file name directly in the text box.
I am relatively certain about two considerations (correct me if you disagree):
1) It's acceptable to handle the OpenFileDialog in the V and send the filename to the MV via binding. I've seen this answered in other discussions.
2) I'll implement a filehandler class that will open the file, check permissions, format the CSV record, etc.
What I am unsure about is how the file checking should occur. If a file is locked, or has not been selected, or already exists, how can this be communicated to the V so that the user is notified? In my first cut at the app, I simply implemented this logic in the code-behind, which does not seem correct:
private bool CSVReady()
{
if (filenameTextbox.Text == "<no file selected>")
{
MessageBox.Show("Please select an output file.");
return false;
}
if (File.Exists(filenameTextbox.Text))
{
var r = MessageBox.Show("File already exists. Append to it?",
"File Warning",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (r == MessageBoxResult.No)
return false;
try
{
File.OpenWrite(filenameTextbox.Text).Close();
}
catch (IOException)
{
MessageBox.Show("File is already open. Please close it.");
return false;
}
}
else // file does not exist, create it and initialize the column labels.
{
if ((MessageBox.Show("File does not exist. Create it?", "File Creation", MessageBoxButton.YesNo, MessageBoxImage.Warning) ==
MessageBoxResult.No))
return false;
File.Create(filenameTextbox.Text).Close();
File.AppendAllText( // Blah blah blah
}
return true;
}
If you have correctly data bound an instance of your view model to your view's DataContext property, then you can access your view model from your view's code behind simply, like this:
...
DialogResult result = fileDialog.ShowDialog();
if (result == DialogResult.OK) filePath = fileDialog.FileName;
...
ViewModel viewModel = (ViewModel)DataContext; // <--------
viewModel.DoSomethingWithNewFilePath(filePath);
UPDATE >>>
What I am unsure about is how the file checking should occur. If a file is locked, or has not been selected, or already exists, how can this be communicated to the V so that the user is notified?
The way that you have written your file checking process is fine as far as MVVM goes. You're not breaking any rules. It's the 'quick' way to implement such functionality, but of course, there are different levels of coding quality.
In my applications, I have xxxManager classes that perform a variety of functionality for me... some might call them service classes. As such, I have a WindowManager class that handles all Window related tasks, including showing MessageBoxes and various dialogs. The reason for this is that I can interface these classes and provide mock implementations for testing, so that the tests don't really open Windows that need someone to close.
If you're not testing and not likely to want to change or add a web interface, then you really don't need that level of separation. If however, you are working on a business application, then it is generally considered good practice to separate the various concerns of the application into different folders or projects; data access, data manipulation, services and UI.
I also have a HardDriveManager class that enables me to perform all of those System.IO functions from the view model without having to add usings for the relevant dlls. So the answer is that what you're doing is ok, but you could split out the different functionality into different helper classes.
I'm using the TestStack.White framework for a WinForms .Net 2.0 C# application's coded automated regression tests. I have no problem handling OK/Cancel message boxes (MessageBox.Show(...)) but I'm having trouble handling the Yes/No buttons when using MessageBoxButtons.YesNoCancel. Thing is, I do not know what the button names are.
These work in my test jigs:
public ButtonTestJig OkButton
{
get { return new ButtonTestJig(Window, "okButton"); }
}
public ButtonTestJig CancelButton
{
get { return new ButtonTestJig(Window, "cancelButton"); }
}
Does anybody know the equivalent "cancelButton" for the NO button and the equivalent "okButton" for the YES button? Or perhaps even better, how to find them. My googling failed me.
Thanks
You can use http://uiautomationverify.codeplex.com/releases/view/11366 or inspect.exe (which comes in the windows SDK to see what the automation Id's for those controls are.
What am I doing:
My main intent is to enable user friendly text to speech for personal use on Win 7. Approach should work in Google Chrome, VS and Eclipse.
Code example:
Following code creates global keyboard hook for ctrl + alt + space, called hookEvent. If event fires, it starts/stops speaking clipboard contents ( that can be updated with ctrl + c ).
/// <summary>
/// KeyboardHook from: http://www.liensberger.it/web/blog/?p=207
/// </summary>
private readonly KeyboardHook hook = new KeyboardHook();
private readonly SpeechSynthesizer speaker = //
new SpeechSynthesizer { Rate = 3, Volume = 100 };
private void doSpeaking(string text)
{
// starts / stops speaking, while not blocking UI
if (speaker.State != SynthesizerState.Speaking)
speaker.SpeakAsync(text);
else
speaker.SpeakAsyncCancelAll();
}
private void hookEvent(object sender, KeyPressedEventArgs e)
{
this.doSpeaking(Convert.ToString(Clipboard.GetText()));
}
public Form1()
{
InitializeComponent();
hook.KeyPressed += new EventHandler<KeyPressedEventArgs>(hookEvent);
hook.RegisterHotKey(ModifierKeysx.Control|ModifierKeysx.Alt, Keys.Space);
}
Question:
I would prefer not using the clipboard. Or at least, restoring the value after, something like:
[MethodImpl(MethodImplOptions.Synchronized)]
private string getSelectedTextHACK()
{
object restorePoint = Clipboard.GetData(DataFormats.UnicodeText);
SendKeys.SendWait("^c");
string result = Convert.ToString(Clipboard.GetText());
Clipboard.SetData(DataFormats.UnicodeText, restorePoint);
return result;
}
What are my options?
Edit:
To my surprise, I found that my clipboard reader is the best way to go. I created a notification area app, that responds to left click (speaking clipboard) and right click (menu opens up). In menu the user can chance speed, speak or create a audio file.
MS provide accessibility tools that do cover what you're trying to do. If you take a look at documents about screen scraping. In short, every component is accessible in some manner, if you use some of the windows debugging tools you can get to see the component names/structures within. You can then use that, however, its complicated as most times you would need to be very specific for each application you intend to scrape from.
If you manage to scrape you dont need to use the clipboard, as you can access the text property of the apps direct. Its not something I've had to do, hence, Ive no code to offer off the top of my head, but the term "screen scraping" should point you in the right direction.
If to expand a little on what Bugfinder said, Microsoft provider a UI Automation Framework to solve problems like the one you mentioned:
In particular you can use the TextSelectionChangedEvent of TextPattern:
The problem with this solution is that it only works on supported operating systems and applications - and not all support this.
Your clipboard solution is acceptable for applications that do not provide a good automation interface.
But for many applications the UI Automation Framework will work well and will provide you with a far better solution.