This is my first MVVM app, and I am wondering how to switch to another view after the user is done with the OpenFileDialog.
The changing view technique currently using is borrowed from here.
In other word, how to call :
private void ExecuteGridViewCommand()
{
CurrentViewModel = MainViewModel._gridViewModel;
}
The problem rises since I couldn't track when the user clicks the Open button of the Dialog since the Dialog is not a XAML control.
private static ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (_currentViewModel != value)
{
_currentViewModel = value;
OnPropertyChanged();
}
}
}
Somebody earlier sent an answer (deleted) that was inspiring (thanks) even though it didn't work out of the box, maybe due to instantiating a new instance of ViewModelLocator().
It was something like this:
private readonly MainViewModel _mainViewModel = (new ViewModelLocator().Main);
_mainViewModel.ExcuteGridView();
After tweaking, now I have this, and it works:
ViewModelLocator.Main.ExcuteGridView();
In order for this to work though, I had to declare the Main as static inside the ViewModelLocator:
public static MainViewModel Main
{
get
{
return _main;
}
}
Related
I am tasked with implementing a simple confirmation dialog for trying to close the main window of my application, but only while a specific process is running. Closing the application means aborting that process. If that process is not running no confirmation is needed.
At first I just created an interface to get Application.Current.MainWindow and used the Closing event of that, but my teacher came up with something else, I just can't find the right way to finish it.
There already is a CanClose method in the code base I use, which is empty by default. It's in the AppInit class where the MainWindow is opened. What I did in the constructor is this:
[ImportingConstructor]
public AppInit(MainWindow mainWindow, [ImportMany] IEnumerable<IClosingValidator> closingValidator)
{
this.mainWindow = mainWindow;
this.closingValidator = closingValidator;
}
That was the idea of my teacher. Now in the CanClose method I can iterate through these closing validations and return true or false, like so:
public Boolean CanClose()
{
foreach (var validation in this.closingValidator)
{
var result = validation.CanClose();
if (result == false)
{
return false;
}
}
return true;
}
The ClosingValidator currently looks like the following, which I think is wrong, but I need to build the dialog somewhere. The whole thing works, but the flag indicating whether the process in question is running is in the ViewModel of another project, meaning this confirmation dialog is always shown.
[Export(typeof(IClosingValidator))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ClosingValidator : IClosingValidator
{
private readonly IMessageDialog messageDialog;
private readonly IOverlayEffect overlayEffect;
[ImportingConstructor]
public ClosingValidator(IMessageDialog messageDialog, IOverlayEffect overlayEffect)
{
this.messageDialog = messageDialog;
this.overlayEffect = overlayEffect;
}
public Boolean CanClose()
{
using (this.overlayEffect.Apply())
{
var result = this.messageDialog.ShowDialog(
new MessageDialogArgs(Resources.Resources.ConfirmClosingApplicationTitle,
Resources.Resources.ConfirmClosingApplicationMessage,
MessageBoxButton.YesNo,
MessageBoxImage.SystemQuestion));
if (result == MessageBoxResult.Yes)
{
return true;
}
}
return false;
}
I think my question comes down to this:
Where do I build my dialog and how do I use a boolean flag from the ViewModel to determine whether to show the dialog in the first place? I'm relatively new to MEF, so sorry anything's unclear.
Edit: I think the idea was that I can use that interface to implement further closing validations at some point in the future.
EDIT 3:
Simplified ViewModel implementation (The actual viewModel has too many parameters):
[Export]
public class TocViewModel
{
[ImportingConstructor]
public TocViewModel(MeasurementSequenceExecution measurementSequenceExecution)
{
this.measurementSequenceExecution = measurementSequenceExecution;
}
public Boolean CanApplicationClose
{
get { return !this.measurementSequenceExecution.IsMeasurementSequenceRunning; }
set
{
this.measurementSequenceExecution.IsMeasurementSequenceRunning = !value;
}
}
The IClosingValidator implementation:
[Export(typeof(IClosingValidator))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ClosingValidator : IClosingValidator
{
[ImportingConstructor]
public ClosingValidator()
{
}
public Boolean CanApplicationClose { get; }
The IClosingValidator interface:
public interface IClosingValidator
{
Boolean CanApplicationClose { get; }
}
The class handling the closing:
[Export(typeof(IApp))]
public class AppInit : IApp
{
[ImportingConstructor]
public AppInit(MainWindow mainWindow,
[ImportMany(typeof(IClosingValidator))] IEnumerable<IClosingValidator> closingValidatorClients,
IOverlayEffect overlayEffect,
IMessageDialog messageDialog)
{
this.mainWindow = mainWindow;
this.closingValidatorClients = closingValidatorClients;
this.overlayEffect = overlayEffect;
this.messageDialog = messageDialog;
}
public Boolean CanClose()
{
if (this.closingValidatorClients != null)
{
foreach (var client in this.closingValidatorClients)
{
if (!client.CanApplicationClose)
{
using (this.overlayEffect.Apply())
{
var result = this.messageDialog.ShowDialog(
new MessageDialogArgs(Resources.Resources.ConfirmClosingApplicationTitle,
Resources.Resources.ConfirmClosingApplicationMessage,
MessageBoxButton.YesNo,
MessageBoxImage.SystemQuestion));
if (result == MessageBoxResult.Yes)
{
return true;
}
return false;
}
}
}
}
return true;
}
private readonly IEnumerable<IClosingValidator> closingValidatorClients;
private readonly MainWindow mainWindow;
private readonly IMessageDialog messageDialog;
private readonly IOverlayEffect overlayEffect;
UPDATE
I made it work, the suggestion to Export the ViewModel with typeof(IClosingValidator) was right, I just had to add a second Export, not replace the default one, didn't know you could have 2 of them. So now with 2 ExportAttributes it works!
I think your implementation of IClosingValidator is at the wrong place.
I have a similar project and what I do is the following:
I have a Interface like you IClosingValidator:
public interface IConfirmShellClosing
{
/// <summary>
/// Gets a value that indicates whether the shell window can be closed.
/// </summary>
bool CanShellClose { get; }
}
This interface is implemented by all ViewModels which should be asked, if the shell can be closed. In your case all ViewModels where the process can be running should implement this interface. So each ViewModel for itself will implement the CanShellClose Property and decide if the process is running in this context. Only if all ViewModels return true for this, the Window can be closed.
Then, in your instance of the window, you can subscribe to the WindowClosing Event and ask all registered ViewModels, if the Window can be closed. This implementation goes to your ViewModel of the Window (or the code behind file):
[ImportMany(typeof(Lazy<IConfirmShellClosing>))]
private IEnumerable<Lazy<IConfirmShellClosing>> _confirmShellClosingClients;
private void ExecuteWindowClosing(CancelEventArgs args)
{
if (_confirmShellClosingClients != null)
{
foreach (var client in _confirmShellClosingClients)
{
if (!client.Value.CanShellClose)
{
// Show your Dialog here and handle the answer
}
}
}
}
I hope this will help.
EDIT:
Okay, you still have a few mistakes in the implementation of your ViewModel and the Validator.
First of all, the Class ClosingValidator is not needed anymore. You want to give the responsibility to the ViewModels, and not to a central validator class.
After that, we need to change the ViewModel like this:
[Export]
public class TocViewModel : IClosingValidator
{
[ImportingConstructor]
public TocViewModel(MeasurementSequenceExecution measurementSequenceExecution)
{
this.measurementSequenceExecution = measurementSequenceExecution;
}
public Boolean CanApplicationClose
{
get { return !this.measurementSequenceExecution.IsMeasurementSequenceRunning; }
set
{
this.measurementSequenceExecution.IsMeasurementSequenceRunning = !value;
}
}
What is happening now? The ViewModel implements the IClosingValidator Interface, so it has to implement the CanShellClose Property. In this property, you can define the logic, in which this ViewModel can decide if the Shell can be closed or not. If you ever add an other ViewModel, it can implement the interface as well and has a different logic for the closing.
In the Importing in the Application itself, all classes which are implementing the IClosingValidator interface are imported and then asked, if the porperty is true or false.
EDIT 2:
I have 3 .dll (HelpingProject, InterfaceProject and ViewModelProject) All 3 should be in the directory where the compositionContainer is searching. In my case, I built the container like this:
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Projects\HelpingSolution\HelpingWpfProject\bin\Debug"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
So the Debug folder will be scanned to find matching exports. I would recommend you search for the this code in your codebase and have a look at where your container is looking for the exports. This does not have to be only one folder, but can also be a plugin folder or different locations. You can find a pretty good introduction here.
Say I have a class that receives data over a TCP stream, parses it and changes it's properties accordingly.
public static class SomeClass
{
static bool myBool;
static string myMessage;
public static void ToggleBool()
{
myBool = !myBool;
// Do some other stuff here
}
public static UpdateMessage(string message)
{
System.Diagnostics.Debug.WriteLine(message);
ProcessMessage(message);
myMessage = message;
}
}
Now what I want to do is have a WPF "Debugging Window" that will visually display the settings. I want to basically run a loop that updates parts of the window accordingly.
Something like:
public partial class LogWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public Async Task UpdateUI()
{
while(checkForUpdates)
{
myCheckbox.IsChecked = await SomeClass.UpdatedBoolValue();
string newMessage = await SomeClass.NewMessageRCVD();
txtBox.Append(newMessage);
}
}
}
But that has 2 obvious issues. One, I have no idea how I would make a function that doesn't burn CPU by constantly checking with a while loop. I imagine I could use a getter/setter approach though. Two, I have to update both in order for that loop to run again.
What's the best approach to this? How do update just the parts of the UI that need to be updated?
EDIT: Similar question: Write an Async method that will await a bool
Depends on how complex a implementation/your needs are.
From your example if you made SomeClass implement INotifyPropertyChanged you could easily attach a WPF window to it, and through binding the window would update automatically without any form of a loop.
If your talking about multiple classes and you want to have them all display the property information in the same window, your best bet would probably be to create a queue. In each property you wish to keep track of have the setter write to the queue. (global or singleton) Then you can easily front that information in a window, or multiple via an Observer pattern. Can also set it up to it never writes to the queue in production, or with conditional compile statements production wouldn't even have the code if that is your desire.
The best way to do this is with data binding.
So we need to first define where our data is coming from. This is called the Context. This is going to come from a ViewModel which is an MVVM term. If you aren't aware of MVVM, don't worry, this can just come from any class you have. In the backend .xaml.cs code we need to add the class to our windows's DataContext. Here's what that looks like:
public partial class DebugView : Window
{
public DebugView()
{
InitializeComponent();
DataContext = new DebugViewModel();
}
}
And in our WPF's XAML file for the window we will have a label and textbox that is defined as such:
<Label Content="{Binding ClientCount, FallbackValue='Clients: 00'}" ... />
<TextBox Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}" ... />
The text of a label is it's "content" while the text of a textbox is just "text." We add the binding keyword in there and now the text for each will be linked to the variables ClientCount and Port, repstively. So our DebugViewModel class will look like this at first:
private string _ClientCount;
public string ClientCount
{
get { return _ClientCount; }
set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
}
private string _Port;
public string Port
{
get { return _Port; }
set { _Port= value; RaisePropertyChanged("Port"); }
}
Now you don't have a Function called RaisePropertyChanged() so what I did (and I think is common practice) was I made a base class that implements the INotifyPropertyChanged and handles all the work there.
So our base class, called BaseViewModel will inherit from the INotifyPropertyChanged class, and setup everything for us. That just looks like this (feel free to just copy paste and use as is):
using System.ComponentModel;
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string prop)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
// Other functions we want all ViewModels to have
}
and so then our DebugViewModel class will look like such:
public class ServerViewModel : BaseViewModel
{
private string _ClientCount;
public string ClientCount
{
get { return _ClientCount; }
set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
}
private string _Port;
public string Port
{
get { return _Port; }
set { _Port= value; RaisePropertyChanged("Port"); }
}
public DebugViewModel()
{
// Initialize to default values
ClientCount = $"Clients {server.clientCount}";
Port = $"{server.port}";
}
// Rest of code
}
And then when you start your program it will autopopulate the fields and you when you change the data in the textbox, the string will change, and vice versa. The UpdateSourceTrigger=PropertyChanged part of our XAML declaration makes it so that the variable is updated as soon as the data in the textbox is changed (default behavior is when the textbox loses focus. e.g. you tab to the next textbox or you click away).
This is pretty cool because you can validate input dynamically as it's typed, as well as not having to worry about switching to the UI thread to update the UI, and IMO makes the code look simpler just by having it bound like this.
I'm working on a multiple document viewer (a simple window with a custom control, each with a separate viewmodel). When clicking on a filename, a new instance of the user control is added to the main window. The user control has a dependency property which holds the path to the filename, defined in it's code-behind. Now i'm struck on how to get the value of this property from the user control to the viewmodel, so it can show the actual document. Any Hints?
<ctrl:DocViewerControl x:Key="docviewer" DocumentSource="{Binding SelectedItem.Path, ElementName=docList}"/>
I use this code in main window to make new instances of my user control where DocumentSource is the dependency property i need access to, as stated above.
Edit:
Following is the (relevant) code for the view and the viewmodel of my control, specific to the dependancy property value capture problem i have.
UserControl.xaml.cs
public partial class ToolboxControl : UserControl
{
public static readonly DependencyProperty DocumentSourceProperty = DependencyProperty.Register("DocumentSource",
typeof(string), typeof(ToolboxControl), new UIPropertyMetadata(new PropertyChangedCallback(OnDocumentSourceChanged)));
public ToolboxControl()
{
InitializeComponent();
}
public string DocumentSource
{
get { return (string)GetValue(DocumentSourceProperty); }
set { SetValue(DocumentSourceProperty, value); }
}
private static void OnDocumentSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
}
}
PV_ViewModel.cs
public class PV_ViewModel : ObservableObject
{
.....
public string DocumentSource
{
get { return (String.IsNullOrEmpty(_documentsource)? (_documentsource = #"about:blank") : _documentsource); }
set { SetField<string>(ref _documentsource, value, "DocumentSource"); }
}
.....
public PV_ViewModel()
{
PropertyChanged += DocumentSourceChanged;
}
.....
protected void DocumentSourceChanged(object sender, PropertyChangedEventArgs e)
{
if (sender != null)
{
switch(e.PropertyName)
{
case "DocumentSource":
{
// show the document and whatsoever
break;
}
}
}
}
.....
}
Neither the getter nor the setter of the viewmodel DocumentSource property get accessed from anywhere, despite the UserControl in MainWindow had is DocumentSourceProperty filled in with the current document path string. (i can see it form a collection of currently opened document on the main app).
To clarify: the application solution contains MainWindow project (the main view, a simple window with a TreeView and the UserControl container), the UserControl project (the (hopefully) standalone application used to show a single document when providing the path to the doc to show through the DocumentSource property.
I am not really sure I understand your problem (or if you understand how Dependency Properties work), so you may have to post a bit more of your code behind (with the DI for example)
Typically your DocViewerControl looks like this
public abstract class DocViewerControl : UserControl
{
public string Path
{
get { return (string)GetValue(PathProperty); }
set { SetValue(PathProperty, value); }
}
public static readonly DependencyProperty PathProperty =
DependencyProperty.Register("Path", typeof(string), typeof(DocViewerControl), new PropertyMetadata(string.Empty));
}
This will expose a Property in XAML of the control.
It's important here that you make it TwoWay binding, so any change from the UserControll will update the bounded field in your ViewModel.
Your ViewModel:
public class Doc1ViewModel : ViewModelBase {
private string path;
public string Path
{
get { return path;}
set {
if(path!=value) {
path = value;
OnPropertyChanged("Path");
}
}
}
}
Now, each time when you assign the property in your UserControl, the value in the ViewModel will be updated. As you can see, the Dependency Property consists from two properties. One static Dependency Property called PathProperty and one instance property called Path.
But having a closer look at it, it's not a real instance property at all. It just wraps calls around the Dependency Property by using GetValue and SetValue (which are derived from DependencyObject class, which every UI control inherits).
Hope this clears it up how Dependency Properties work, as it hard to tell what's wrong with your approach without seeing the code used.
In a nutshell, Dependency Properties (together with Attached Properties) extend the XAML code with TwoWay bindable properties (normal instance property can only be bound in one direction).
Here, I have a bit confusion about UI language. If language is changed then what happens? The whole folder gets changed or Culture gets loaded? I cannot get what is actually happening.
Properties.Strings.MainWindow_Language_Selection_English_Label="English"
Properties.Strings.MainWindow_Language_Selection_Gujarati_Label="ગુજરાતી"
Please explain what is happening.
private void LanguageSelection_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem item = LanguageSelection.SelectedItem as ComboBoxItem;
if (item.Content.ToString() == Properties.Strings.MainWindow_Language_Selection_English_Label)
{
CultureManager.UICulture = new System.Globalization.CultureInfo("en");
}
else if (item.Content.ToString() == Properties.Strings.MainWindow_Language_Selection_Gujarati_Label)
{
CultureManager.UICulture = new System.Globalization.CultureInfo("gu");
}
Settings.Default["UILanguage"] = CultureManager.UICulture.Name;
Settings.Default.Save();
}
In general, setting the culture on application thread will be effective on the next form that is displayed, so to make this work you probably need a login/language selection window where you set the main thread's culture and then show application's main window.
There were a few attempts around this to make language selection take effect immadiately (easier in WPF) but this is how it works out of the box.
In WPF, however, if you are directly binding UI elements to resources you can make the UI update by raising a property change event on your resource property. The easiest way to achieve this (other than creating a new code generator for the .resx file) would be to wrap your resources in a model class like this:
public class StringRes : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate {};
public string Login
{
get { return Properties.Strings.Login; }
}
public string Password
{
get { return Properties.Strings.Password; }
}
public void NotifyLanguageChanged()
{
PropertyChanged(this, new PropertyChangedEventArgs("Login"));
PropertyChanged(this, new PropertyChangedEventArgs("Password"));
}
}
public class MainWindow
{
private StringRes _resources;
private void LanguageSelection_SelectionChanged()
{
System.Threading.Thread.CurrentThread.CurrentUICulture = GetCurrentCulture();
_resources.NotifyLanguageChanged();
}
}
If you have bound your UI elements to the instance of the StringRes class, they will be updated when you raise the notification change event in your model.
If, on a WPF window, you have more than 1 {Binding} for a parameter, is it possible to name them ?
For example for images i have (in the XAML part): Visibility="{Binding}" but also on some textboxes i want to make IsEnabled="{Binding}"
Is there some way to name them ? So that in the code in the backside (.cs side) it will pick the right one ?
Thanx in advance.
You can simply have view model wrapping those properties into single object:
public class ViewModel : INotifyPropertyChanged
{
private bool imagesVisibility;
private bool isTextBoxEnabled;
public event PropertyChangedEventHandler PropertyChanged;
public bool ImagesVisibility
{
get { return this.imagesVisibility; }
set
{
this.imagesVisibility = value;
this.PropertyChanged(this,
new PropertyChangedEventArgs("ImagesVisibility"));
}
}
public bool IsTextBoxEnabled
{
// ... similar as with ImagesVisibility property implementation
}
}
Note that you'll also need a boolean to visibility converter, which examples of you can find on StackOverflow (here)... or elsewhere.
Then, you simply set instance of ViewModel to your form data context:
public MyForm()
{
InitializeComponent();
DataContext = new ViewModel();
}
You can then do images binding like Visibility="{Binding ImagesVisibility}" and textbox IsEnabled="{Binding IsTextBoxEnabled}".