I have a timer in my wpf application wich every 5 minutes ask a WCF service. If the service have message for my application, I get a list which contains text data and a specific code.
This code give an information about the view which must be loaded to print the data.
I have two ViewModel (the data source is the same for both): One for a Ticker > one view and One for Popup > two view
Project files :
View
Popup
PopHighView.xaml
PopMediumView.xaml
Ticker
TickerLayout.xaml
TickerNormal.xaml
ViewModel
PopViewModel
TickerViewModel
Models
AlertModel.cs
ViewParsers
AlertParser.cs
Datasource :
public class AlertParser : IAlertParser{
AlertServiceClient service;
public List<AlertModel> TickerAlertData()
{
try
{
service = new AlertServiceClient();
List<AlertModel> items = (from item in service.GetActiveAlert() select new AlertModel
{
Alertid= item.AlertId,
Alertstartdate = item.AlertStartDate,
Alerttitle = item.AlertTitle,
Alerttxt = item.AlertText
}).ToList();
return items;
}
catch
{
return null;
}
}
}
When my application is launched, there is no loaded view, only a icon in the systray(with wpf notifyicon).
My problem is, under theses circonstances, I don't understand how I could loaded a couple ViewModel/View, and pass the data to them, when my timer return a true from my service.
Many examples on the web have a mainview loaded, that's why I'm lost (like Conductor example on caliburn micro page).
Thanks for any help !
Edit :
Ok, My timer look like that :
if (service.IsAlertReady()=true)
{
string hostName = Dns.GetHostName();
string myIP = Dns.GetHostEntry(hostName).AddressList[0].ToString();
service.IsAlertForMe(myIP);
if(service.IsAlertForMe(myIP) == true)
{
ShellViewModel shell = new ShellViewModel();
shell.ShowMediumPop();
}
else
...
ShellViewModel
public class ShellViewModel : Conductor<object>
{
public void ShowMediumPop()
{
ActivateItem(new PopViewModel());
}
}
PopViewModel
public class PopViewModel : screen
{
protected override void OnActivate()
{
base.OnActivate();
}
}
PopView.Medium
<UserControl x:Class="TerminalClientProto.View.PopView"
...
cal:View.Model="{binding}"
cal:View.Context="Medium"
>
I'm sorry but I don't understand how I could launch my view when my Ticker tick. I've read the documentation, but I need some hints to understand this mechanism.
A program, any program, including the very program that contains the views you want to display can show a view in a number of ways. Here's a few:
var app = new App();
app.InitializeComponent();
app.Run();
Or you can start the view directly:
var view = new MyView();
view.Show();
// or
view.ShowDialog();
If the view is a MainWindow, then you can create a ContentControl area within the view to inject the Usercontrol containing the sub-view of what you want displayed. This still requires the MainWindow to be open... So the examples above would also work when injecting UserControls into a MainWindow. The act of injecting a User control is setting the ContentControl's Content to an instance of the User Control itself. Eventhandlers can handle this scenario nicely...
public void NewUserControlInMainWindow(object sender, UserControl uc){
//XCC = the Xaml content control in main window
XCC.Content = uc;
}
I'm not really sure how Caliburn does view injection....
Related
I am trying to set a TextBox value text owned by MainWindow (WPF Window) from another class in the same namespace, but nothing happens. I've gone through many suggestions and answers to similar questions with no avail. What puzzles me is that MainWindow's SetText receives the text as it is being displayed in Visual Studio console, but doesn't have any effect on the actual textbox. The 'ConsoleLog' textbox has the default XAML values.
So my MainWindow is similar to this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string SetText
{
get { return ConsoleLog.Text; }
set {
Console.WriteLine(value);
ConsoleLog.Text = value;
}
}
}
In my App class:
public partial class App : Application
{
private void Test()
{
var mw = new MainWindow();
mw.SetText = "Please display something!";
}
}
I tried with several other methods such as:
((MainWindow)System.Windows.Application.Current.MainWindow).ConsoleLog.Text = "Please display something!";
but nothing works so far without any error messages.
I'm fairly new to WPF C# and I'm sure it is something very obvious I'm missing, but it drives me crazy.
WPF project Default project start up MainWindow by configuring its App.xaml file, not by app.xml.cs. if you want to get access to MainWindow , you should initiate MainWindow manually by following code
public App()
{
var mw = new MainWindow();
mw.SetText = "Please display something!";
mw.ShowDialog();
}
and remove the following line from App.xaml
StartupUri="MainWindow.xaml"
if you want to get started with WPF application and make a good app,I recommend you to see some basic tutorial, and try to learn MVVM (or MVC in web) programming methodology.
I am stuck with a problem where I want to change user control on different events in background. I am new in MVVM but I am bound to use MVVM only to achive this task. Code structure is little complex to me but still I figured that New Employee form is getting shown on button click but in new window but I want that form to be opened in current window's content. Code is given here which I need to modify to open usercontrol.
public Task<bool?> InitModification(CoreViewModel vm)
{
var tcs = new TaskCompletionSource<bool?>();
_dispatcherService.CurrentDispatcher.BeginInvoke(new Action(() =>
{
bool? result = null;
Window activeWindow = null;
for (var i = 0; i < Application.Current.Windows.Count; i++)
{
var win = Application.Current.Windows[i];
if ((win != null) && (win.IsActive))
{
activeWindow = win;
break;
}
}
if (activeWindow != null)
{
var win = new NewEmp(vm) { Owner = activeWindow };
result = win.ShowDialog();
}
tcs.SetResult(result);
}));
return tcs.Task;
}
My answer will not use your code since I don't find it useful to what you want to achieve. My answer will suffice with a suggestion on how you can achieve changing content control with MVVM
The way I go about it is with every MVVM-project I have a "application-shell" that acts as a wrapper for all my other content and through that you can easily change content. This application is a View and a ViewModel like such.
ShellView
<xmlns:ViewModel="clr-namespace:WhereOurViewModelIs.ViewModel">
<!--More XAML code-->
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:MyViewModel}">
<Views:MyView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:AnotherViewModel}">
<Views:AnotherView/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPage}"/>
ShellViewModel
public class ShellViewModel
{
private BaseViewModel _currentPage{get;set;}
Public BaseViewModel CurrentPage{
get{return _currentPage;}
set{_currentPage = value; OnPropertyChanged();}
}
public ShellViewModel
{
CurrentPage = new MyViewModel();
}
}
Since we don't know how many different pages there is (in theory) we will tell them that need to be a of a type (inherited or an object) of BaseViewModel. This way we don't need to check for every single page and remove redundant code.
Then you set the Datacontext = new ShellViewModel(); in behind code of ShellView.xaml
BaseViewModel
public class BaseViewModel
{
/*This class can contains whatever you want your other ViewModels to be able to do*/
}
Now you need to set up 2 ViewModels with 2 Views just like we did with our shell.
MyViewModel
public class MyViewModel:BaseViewModel
{
/*Contains Properties,methods,private fields. What you want to show on view*/
}
AnotherViewModel
public class AnotherViewModel:BaseViewModel
{
/*Contains Properties,methods,private fields. What you want to show on view*/
}
Now you can set event to your ShellViewModel to change content whenever something happens. Hopefully this can atleast give you some idea how to work with MVVM. Of course you need to set up our ViewModels with properties changed event and other to get everything working, but this is a start for you.
If you find this answer helpful please chose it as an answer since it take some time to make an example like this.
I'm new on Caliburn Micro and want some advice on which path to take to devolop my app interface and navigation between views.
My idea is to have a MainWindow which will contain a menu of buttons, each one related with a specific view. Each view will be stored in a separated WPF UserControl. The mainWindow will also contain a TabControl bound to an ObservableCollection of tabs on viewmodel. Everytime a button on menu is clicked, I want to add a new tab with a ContentPresenter inside that will dynamically load a view and its corresponding viewmodel.
So my questions:
1) Should I use a Screen Collection here?
2) Should the UserControl implement Screen interface?
3) How do I tell MainWindow ViewModel which view to load on the new added tab maintaining viewmodels decoupled?
Thanks to everyone in advance.
UPDATE
After a lot of reading and some help of the community I managed to resolve this. This is the resultant AppViewModel:
class AppViewModel : Conductor<IScreen>.Collection.OneActive
{
public void OpenTab(Type TipoVista)
{
bool bFound = false;
Screen myScreen = (Screen)Activator.CreateInstance(TipoVista as Type);
myScreen.DisplayName = myScreen.ToString();
foreach(Screen miItem in Items)
{
if (miItem.ToString() == myScreen.ToString())
{
bFound = true;
ActivateItem(miItem);
}
}
if (!bFound) ActivateItem(myScreen);
}
public ObservableCollection<MenuItem> myMenu { get; set; }
public ObservableCollection<LinksItem> myDirectLinks { get; set; }
public ICommand OpenTabCommand
{
get
{
return new RelayCommand(param => this.OpenTab((Type) param), null);
}
}
public AppViewModel()
{
OpenTab(typeof(ClientsViewModel));
MenuModel menu = new MenuModel();
myMenu = menu.getMenu();
myDirectLinks = menu.getLinks();
}
public void CloseTab(Screen param)
{
DeactivateItem(param, true);
}
}
I have to keep the ICommand from OpenTabCommand because the name convention of Caliburn.micro doesn't seems to work inside DataTemplate. Hope it could help someone else. Thanks to all
I've done something very similar using Caliburn.Micro, and based it on the SimpleMDI example included with the examples, with a few tweaks to fit my needs.
Much like in the example, I had a main ShellViewModel:
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
}
with a corresponding ShellView containing a TabControl - <TabControl x:Name="Items">, binding it to the Items property of the the Conductor.
In this particular case, I also had a ContextMenu on my ShellView, bound (using the Caliburn.Micro conventions), to a series of commands which instantiated and Activated various other ViewModels (usually with a corresponding UserControl, using the ActivateItem method on the Conductor.
public class YourViewModel: Conductor<IScreen>.Collection.OneActive
{
// ...
public void OpenItemBrowser()
{
// Create your new ViewModel instance here, or obtain existing instance.
// ActivateItem(instance)
}
}
In that case, I didn't require the ViewModels to be created with any particular dependency, or from any other locations in the program.
At other times, when I've needed to trigger ViewModel from elsewhere in the application, I've used the Caliburn.Micro EventAggregator to publish custom events (e.g. OpenNewBrowser), which can be handled by classes implementing the corresponding interface (e.g. IHandle<OpenNewBrowser>), so your main ViewModel could have a simple Handle method responsible for opening the required View:
public class YourViewModel: Conductor<IScreen>.Collection.OneActive, IHandle<OpenNewBrowser>
{
// ...
public void Handle(OpenNewBrowser myEvent)
{
// Create your new ViewModel instance here, or obtain existing instance.
// ActivateItem(instance)
}
}
This section of the documentation will probably be useful, especially the Simple MDI section.
Additional code I mentioned in the comments:
I sometimes use a generic method along these lines ensure that if I have an existing instance of a screen of a particular type, switch to it, or create a new instance if not.
public void ActivateOrOpen<T>() where T : Screen
{
var currentItem = this.Items.FirstOrDefault(x => x.GetType() == typeof(T));
if (currentItem != null)
{
ActivateItem(currentItem);
}
else
{
ActivateItem(Activator.CreateInstance<T>());
}
}
Used like:
public void OpenBrowser()
{
this.ActivateOrOpen<BrowserViewModel>();
}
This is more of a conceptual question rather than a practical one. I'm just starting to learn the MVVM concept for developing UI , and I've come across a dillema I'm not sure the answer to:
Say I have a main window and a little pop-up window (meaning it's a small window with some UI elements in it). The structure of the program will look something like this:
MainWindow
model <-- MainWindowViewModel.cs <-- MainWindowView.xaml (containing no code-behind)
PopUpWindow (A UserControl)
model <-- PopUpWindowViewModel.cs <-- PopUpWindowView.xaml (containing no code-behind)
*the model is just a bunch of BL classes that are irrelevant for this question.
Now , lets say I want to create a new PopUp window from inside the MainWindowViewModel (or even save an instance of it in a private data-member). What is the correct way of doing so?
The way I see it I can't do something like this :
PopUpWindow pop = new PopUpWindow()
Because it kind of defeats the purpose of abstracting the view from the view model(What if a year from now i'll want to create a better version of the PopUpWindow using the same PopUpWindowViewModel?).
On the other hand , I can't initialize a new instnace of the PopUpWindow using just it's view model (The viewModel as I understand is not supposed to know anything about the view that will use it).
Hope it all makes sense... so what would you do in that situation?
*Just to clarify it further , let's say for argument's sake that the situation I'm describing is a button on the MainWindowView that upon clicking will open a PopUpWindowView.
Thanks in advnace.
I had somewhat a similar dilemma and I'll explain how I solved it.
Let's say you have MainWindow and a SettingsWindow, which you want to display when the SettingsButton is clicked.
You have two respective view models, MainWindowViewModel and SettingsViewModel, which you will be passing as their Window.DataContext properties.
Your MainWindowViewModel should expose an ICommand property named SettingsButtonCommand (or similar). Bind this command to the SettingsButton.Command.
Now your command should invoke something like this:
void OnSettingsButtonClicked()
{
var viewModel = new SettingsViewModel();
var window = new SettingsWindow();
window.DataContext = viewModel;
window.Show();
}
There is a slight issue when you want to use Window.ShowDialog(), because you need to resume execution.
For these cases I have an asynchronous variant of the DelegateCommand:
public sealed class AsyncDelegateCommand : ICommand
{
readonly Func<object, Task> onExecute;
readonly Predicate<object> onCanExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public AsyncDelegateCommand(Func<object, Task> onExecute)
: this(onExecute, null) { }
public AsyncDelegateCommand(Func<object, Task> onExecute, Predicate<object> onCanExecute)
{
if (onExecute == null)
throw new ArgumentNullException("onExecute");
this.onExecute = onExecute;
this.onCanExecute = onCanExecute;
}
#region ICommand Methods
public async void Execute(object parameter)
{
await onExecute(parameter);
}
public bool CanExecute(object parameter)
{
return onCanExecute != null ? onCanExecute(parameter) : true;
}
#endregion
}
You've specifically said that the popup is a UserControl so you can use basic data templating. First create view models for your main window and popup control:
public class MainViewModel : ViewModelBase
{
private PopUpViewModel _PopUp;
public PopUpViewModel PopUp
{
get { return _PopUp; }
set { _PopUp = value; RaisePropertyChanged(() => this.PopUp); }
}
}
public class PopUpViewModel : ViewModelBase
{
private string _Message;
public string Message
{
get { return _Message; }
set { _Message = value; RaisePropertyChanged(() => this.Message); }
}
}
The MainViewModel's PopUp member is initially null, we'll set it to an instance of PopUpViewModel when we want the popup to appear. To do that we create a content control on the main window and set it's content to that member. We also use a data template to specify the type of child control to create when the popup view model has been set:
<Window.Resources>
<DataTemplate DataType="{x:Type local:PopUpViewModel}">
<local:PopUpWindow />
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Content="Show PopUp" Click="Button_Click_1" HorizontalAlignment="Left"/>
<ContentControl Content="{Binding PopUp}" />
</StackPanel>
I'm doing a big no-no here by creating the view model in the code-behind along with a click handler, but it's just for illustrative purposes:
public partial class MainWindow : Window
{
MainViewModel VM = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this.VM;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.VM.PopUp = new PopUpViewModel { Message = "Hello World!" };
}
}
That's it! Click the button, popup window appears underneath it showing the content. Now it may not always be this simple, sometimes you may want to create multiple children on a parent control...in that case you'd set up an ItemsControl, set its panel to a Grid (say) and modify the data templates to set the Margin etc on each element to position them. Or you may not always know what type of view model is going to be created, in which case you need to add multiple data templates for each type you're expecting. Either way you still have good separation of concerns because it is the views that are deciding how to display the content in the view models. The view models themselves still don't know anything about the views and they can be unit-tested etc independently.
I am working on a Windows Phone 7 application. Now I need to switch the view after a user tapped the designated button which takes user to another view.
Which component, theoretically, in MVVM should be in charge of the navigation, i.e. switching views? Code snippets would be good to show demonstration.
I have tried inserting the switching code in View and it works alright, but I encountered a situation where I call an asynchronous web service and would like to navigate user to the new view only after the operation is done, the navigation code should be inside the event handler.
Thank you.
P/S: My project's deadline is coming soon, I have no time to rebuild my project using MVVM tools, such as MVVM Light, Caliburn Micro, and etc.
I put a Navigate methods in the base class that all my ViewModel's share:
protected void Navigate(string address)
{
if (string.IsNullOrEmpty(address))
return;
Uri uri = new Uri(address, UriKind.Relative);
Debug.Assert(App.Current.RootVisual is PhoneApplicationFrame);
BeginInvoke(() =>
((PhoneApplicationFrame)App.Current.RootVisual).Navigate(uri));
}
protected void Navigate(string page, AppViewModel vm)
{
// this little bit adds the viewmodel to a static dictionary
// and then a reference to the key to the new page so that pages can
// be bound to arbitrary viewmodels based on runtime logic
string key = vm.GetHashCode().ToString();
ViewModelLocator.ViewModels[key] = vm;
Navigate(string.Format("{0}?vm={1}", page, key));
}
protected void GoBack()
{
var frame = (PhoneApplicationFrame)App.Current.RootVisual;
if (frame.CanGoBack)
frame.GoBack();
}
So the ViewModel base class executes the navigation if that's what you are asking. And then typically some derived ViewModel class controls the target of the navigation in response to the execution of an ICommand bound to a button or hyperlink in the View.
protected SelectableItemViewModel(T item)
{
Item = item;
SelectItemCommand = new RelayCommand(SelectItem);
}
public T Item { get; private set; }
public RelayCommand SelectItemCommand { get; private set; }
protected override void SelectItem()
{
base.SelectItem();
Navigate(Item.DetailPageName, Item);
}
So the View only knows when a navigate action is needed and the ViewModels know where to go (based on ViewModel and Model state) and how to get there.
The view should have a limited number of possible destinations. If you have to have a top-level navigation on every page, that should be part of your layout or you can put them in a child view.
I put navigation outside of MVVM in a class that is responsible for showing/hiding views.
The ViewModels use a messagebroker with weakevents to publish messages to this class.
This setup gives me most freedom and doesn't put any responsibilities in the MVVM classes that do not belong there.