I have a 3 part question about the "Hello Screens" Caliburn Micro example. (I'm using a WPF port of the project). This concerns the behavior of CustomerView.xaml which contains a "Save" and "Close" button pair. Willing to accept answers to just 1 and 2 as 3 probably becomes trivial at this point!
Whenever the AddressViewModel is activated by the DialogConductorViewModel the Save and Close buttons become disabled. This happens here in the EditAddress function of CustomerViewModel:
Dialogs.ShowDialog(new AddressViewModel());
1) How do the user controls of CustomerView know to become disabled here? My first thought was that CustomerViewModel (CMV) must've deactivated and maybe CM disables the the CustomerView UserControl for us as a result. However, if you override OnDeactivate in CMV the view model isn't being deactivated whenever it shows a dialog with the "ShowDialog" function.
2) From a WPF standpoint how do all the controls on CustomerView become disabled? If I put this code somewhere in CustomerView.xaml and run the project, the CustomerView UserControl remains enabled throughout:
<TextBlock Text="{Binding Path=IsEnabled,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></TextBlock>
However, if I bind to IsEnabled on any control I place onto CustomerView.xaml that control's IsEnabled property is being set to false when a dialog is shown:
<TextBlock Text="{Binding Path=IsEnabled,
ElementName=TryClose }"></TextBlock>
3) I would like to write a style datatrigger to alter other aspects of CustomerView's appearance whenever it's displaying a dialog. Ideally I would be binding to a property on CustomerViewModel but the best I can tell, this ViewModel doesn't inherently know whenever it's displaying a Dialog through the DialogManager unless there's something I've missed. Does CM give me a way to detect whenever this dialog is being displayed from the point of view of the CustVM?
Thanks,
Sean
Related
I have a UserControl with two buttons in my Window. The UserControl has it's own DataContext with two corresponding Commands.
Naturally, the binding on the buttons was written like this
Command="{Binding SaveCommand}"
However, we have two launch mechanisms for our modules: one is via a WPF launcher and the other is via our old WinForms menu. If we use the WinForms menu, the binding does not work anymore. Instead the following Binding was the only way to make it work.
Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Is this due to WinForms in combination with the way commands bubble up or something along those lines?
Note 1: Other modules seem not to be afflicted somehow. It might really be this exact structure / the double DataContext (window/control)
Note 2: It might be worth noting that we use Telerik's RadButton instead of regular buttons.
I have made quite a bit of progress on my first MVVM WPF application, the issue I am now having is I have a Window that has a viewmodel. This window has a button which opens another window that has another viewmodel.
Imagine a textbox on the first window. Once the second is opened the user will select a value and click save, this window will close and update the first window with its value.
When pushing save I have an ICommand on the childwindows Viewmodel that calls the SaveMethod. I have the selected value stored in a property on the Child windows viewmodel. But how do I update the Main Windows textbox with this value? I imagine I bind a property on the main windows view model, but unsure on how to continue.
Please advise, I can provide code examples if needed, but I think I may have explained it well enough, oh and thanks to everyone at StackOverflow for the help on my questions I have learnt a lot.
This is pretty straightforward using the MVVM Light framework. For the purposes of demonstration I'm going to use a string as the value you're passing, but it's easy to construct a different message type for whatever you need to pass.
In the constructor of your first Window's ViewModel you register to receive NotificationMessages. NotificationMessages are used to send string messages:
public MyFirstViewModel()
{
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
In the SaveMethod in your second Window's ViewModel you send a message with the value you want to pass. I'm using MyStringValue as the name of the property that stores the value chosen by the user in your second Window:
private void SaveMethod()
{
MessengerInstance.Send(new NotificationMessage(MyStringValue));
}
When that message is received by the ViewModel of the first Window the NoitificationMessageReceived method is called. I'm going to put that value in a string property on the first ViewModel called MySavedValue:
private void NotificationMessageReceived(NotificationMessage msg)
{
MySavedValue = msg.Notification;
}
In your View for the first Window you have a TextBox with its Text property bound to MySavedValue. This updates whenever MySavedValue is updated.
In the parent viewmodel, you'll need a reference to the child viewmodel. When the child window is closed, you'll want to get the value of the secondviewmodel's property and set it to a appropriate property of the first parent viewmodel.
One of the posible (and simple) solutions is to keep one ViewModel for both windows
<Grid>
<StackPanel>
<TextBox Text="{Binding TheText}" />
<Button Command="{Binding ShowOptionsCommand}" Content="..."/>
</StackPanel>
<Popup IsOpen="{Binding IsShowingOptions}">
<StackPanel>
<ListBox ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption,Mode=TwoWay}"/>
<Button Command="{Binding SaveOption}">Save</Button>
</StackPanel>
</Popup>
</Grid>
//ShowOptionsCommand handler
void ShowOptions()
{
IsShowingOptions = true;
}
//SaveOptionCommand handler
void SaveOption()
{
TheText = SelectedOption;
IsShowingOptions = false;
}
I'm using the Popup to simplify the example.
Personally I'd go with the mvvm light framework already mentioned, but another option is to leverage IOC, also included with the above framework.
With this pattern view models have interfaces and are bound as properties from a view model locator data source. Within that, the child view model can be injected to the parent view model. Because IOC can create singleton instances of objects, the same instance gets given to the parent as is bound to the child window. That way you get a reference to the view model but through an interface thus preserving the separation.
Just offering this as an alternative technical solution beyond those already offered.
I want to use the C# System.Windows.Forms.SendKeys.SendWait() Method to send Keystrokes from an OnScreenKeyboard to a Textbox. Since I may use this OnScreenKeyboard at other places too I created a UserControl with View (for the Design of the Keyboard) and Viewmodel (basically for calling the Sendkeys.SendWait() Method) in an extra project.
Within this project I created a MainView where I included the UserControl via a ContentControl as you can see in the Code below. CurrentPage basically refers to the Viewmodel of the Keyboard.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:KeyboardViewmodel}">
<view:KeyboardView/>
</DataTemplate>
</Window.Resources>
<Grid>
<Border Background="White">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"/>
</Border>
<TextBox Width="120"/>
</Grid>
I now have the OnScreenKeyboard and a Textbox in my Window. Clicking into the Textbox and pressing buttons of my OnScreenKeyboard will result in text appearing in my Textbox. All Controls within the KeyboardView are set to avoid getting focus. This is necessary to maintain focus on the Textbox.
The Buttons in the KeyboardView all bind to the Command ClickCommandin my KeyboardViewmodel. Here is the code of the KeyboardViewmodel:
public class KeyboardViewmodel : BaseModel
{
public BaseCommand ClickCommand { get; set; }
public KeyboardViewmodel()
{
ClickCommand = new BaseCommand(PressAndRelease);
}
public void PressAndRelease(object key)
{
if (((string)key).Length <= 1)
SendKeys.SendWait((string)key);
else
SendKeys.SendWait("{" + (string)key + "}");
}
}
Now I did create a NuGet Package with these Files and imported them to the project where I want to use my OnScreenKeyboard.
I did do basically the same as when I tested the OnScreenKeyboard before.
But let me explain the structure of the project a little more:
I have a MainView + MainViewmodel. The MainViewmodel manages the navigation between available pages. To show these pages I have - as in the short example before - a ContentControl whose content is bound to a CurrentPage Property. The MainViewis a normal Window, all other Views are UserControls.
In one of these pages I need an OnScreenKeyboard (DetailsView + DetailsViewmodel). So it seemed logical to me to use another ContentControl within the DetailsView:
<Border Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Height="Auto" Width="Auto">
<ContentControl Content="{Binding Path=OnScreenKeyboard}"/>
</Border>
I create the KeyboardViewmodel in the constructor of the DetailsViewmodel. The constructor of the DetailsViewmodel is called in the MainViewmodel at startup.
So now everything works out fine so far, the OnScreenKeyboard is shown on the correct page in the correct place. If I click a button of the OnScreenKeyboard the proper bound command is called and the SendKeys.SendWait() Method is called.
But no text appears in the TextBox. I have a very bad understanding of the SendKeys.SendAwait() Method. Also, the MSDN Documentation seems to be not very exhaustive on this topic.
It states: "Sends the given keys to the active application, and then waits for the messages to be processed."
Now. The Active / Focused Application is my Application. So my guess is that the KeyStrokes should be processed by my Textbox.
My Questions:
Any guesses how to debug the 'SenWait()' Method further e.g. track where the strokes are really sent to or something like that?
Is this the correct way for sending KeyStrokes to an active Application? It seems like SendKeys comes from Windows Forms, I use WPF.
Should I just pass my Textbox as reference to the OnScreenKeyboard and write directly to the referenced Textbox? This would make me much less flexible in regards of reusability.
Update:
As pointed out in the comments this could probably be a duplicate question.
I am well aware of the various different solutions and have already considerd them:
http://wpfkb.codeplex.com/
http://www.codeproject.com/Articles/32568/A-Touch-Screen-Keyboard-Control-in-WPF
http://www.codeproject.com/Articles/145579/A-Software-Virtual-Keyboard-for-Your-WPF-Apps
But as one may understand these projects are looking all way too powerfull for my simple needs.
Here a screenshot to provide a better understanding of my needs:
It is really as simple as that. 4 rows of buttons that will never change, no other controls / functionality than sending the CommandParameter of the pressed button to the Textbox / Active Form.
Researching on that specific problem hasn't shown any problems like that. In most other SO Questions the problem is to send Data to another Window, not to send Data WITHIN the current Window.
So I don't consider this question as duplicate.
I have a treeview at the left side of the screen, and when I click on any of the TreeViewItem, I want the right side of the screen to change accordingly.
For example, clicking on 'Project' would display on the right half of the screen, a label for project name along with the project name in a text box, and a similar label-textbox pair for some other fields. Clicking on a sub-option of 'Project' such as 'Task 1' should change the right half of the screen such that instead of labels and textboxes for project name and details, it should now be for task name/details. Atm, I only care about label-textbox pairs but in the future I'll need some more sophisticated options, maybe buttons and tables.
What I thought of was to have a grid premade for each option, when I clicked on 'Project' there would be a grid which displays all the info for a Project. And when I then clicked on 'Task 1', the Project grid should be hidden and the Task grid should be displayed with the fields filled out.
Is this possible? What should I be using to create templates that I can then choose from?
Firoz already mentioned the important bit. A rough guess is that you're not using MVVM pattern, so to minimize the adaption effort, you could add a Content Control to your window and set the content of this control whenever a selection is made. You can put any User Control in there.
Using MVVM would mean you bind that Content Control to a property on your ViewModel (of type UIElement or UserControl) and set an instance whenever a bound selected values changes. Speaking of selected Value, I think the default TreeView is not really Binding-friendly, so you might end up with behaviours that do the binding for you.
What you are asking to do is quite easy and possible, but I don't think you are thinking quite big enough.
As your project grows and the number of different things that you want to show expands, then you are going to need to show and hide more and more controls. This is quite quickly going to get unmanageable. Instead think about some other controls deal with this, in some ways you are doing something very like a tabbed dialog, just with a hierarchical set of tabs.
A tabbed dialog has a panel and a set of tabs, when you click on each tab, the content of the panel changes. In fact you can create UserControls one for each specialised set of UI that you want to display, e.g. you could have a ProjectControl that displays all of your project textboxes, labels, buttons etc.
In addition WPF has this neat feature called DataTemplates, these define how a type of data should look when it is displayed. So if you where to have a
public class MyProject
{
public string Name {get;set;}
}
Then you could define
<DataTemplate DataType="{x:Type MyProject}>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
And WPF will automatically convert the data into to its visual form if you set it as the content of the tab panel.
However this type of displaying content in a panel is not the only WPF control that does this. There is also something called a NavigationFrame, which also can be used wrapped into a Window as a NavigationWindow. This control provides you ways to navigate to the next Page to display. Pages can be just like the UserControls in a tabbed dialog, but can also be URIs, enabling you to link in content from the web if you wish. In addition you can call NavigateTo from other controls enabling you build much more usable interfaces.
I worked through the process of building a full windows control panel style interface in
http://alski.net/post/2012/01/11/WPF-Wizards.aspx
and http://alski.net/post/2012/01/13/WPF-Wizards-part-2-Glass.aspx
I've added later VS2012 style glows in
http://alski.net/post/2013/09/14/WPF-Re-creating-VS2012Office-2013-window-glow.aspx
And then released the entire source code as open source at
http://winchrome.codeplex.com/
This comes with support for embedding Navigation panels with
<WinChrome:SearchableNavigationWindow
x:Class="WinChrome.Win7Demo.MainWindow"
...
xmlns:WinChrome="clr-namespace:WinChrome;assembly=WinChrome"
Style="{StaticResource Win7NavigationWindow}">
<WinChrome:SearchableNavigationWindow.Navigation>
<view:Navigation x:Name="navigationTree"/>
</WinChrome:SearchableNavigationWindow.Navigation>
(Full source code)
Where the navigation window is embedded as, but can also be a TreeView.
<UserControl x:Class="WinChrome.View.Navigation" ...>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" Padding="12,0"
VerticalScrollBarVisibility="Auto" >
<StackPanel>
<Button
Margin="0,12,0,0" Style="{StaticResource LinkNavigatorButtonStyle}"
Content="Home"
Command="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Win7Demo:MainWindow}, AncestorLevel=1},
Path=GoHomeCommand}" />
</StackPanel>
</ScrollViewer>
(Full source code)
I have a ContextMenu as part of a TabControl such as:
<TabControl Name="MyTabControl">
<TabControl.ContextMenu>
<ContextMenu Name="MyContextMenu" IsEnabled="False" StaysOpen="True">
<MenuItem Header="Item1"></MenuItem>
...
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
As you can see, the menu is disabled. It will be enabled later in the program, for now however, it should remain in its disabled state. The problem is, when I right-click the TabControl and the disabled menu shows, it simply stays where it was first opened, no other right-click will move it, nor will a left-click somewhere make it disappear.
The only way to get rid of it, would be either by enabling it and then right/left-clicking, or by using MyContextMenu.Visibility = Visibility.Collapsed/Hidden;
I tried setting the StaysOpen-property to False. Then the menu will open once in its disabled state. After left-clicking disappear and then not appear again even if it gets enabled.
The only way around it could be changing the StaysOpen-property along with the IsEnabled-property, but it is a bit weird that the menu opens exactly once in its disabled state and then not anymore.
I hope anybody could explain to me, why exactly a disabled menu won't close and the property StaysOpen at false makes it open exactly once, because it seems like a weird behaviour to me.
It seems that the behaviour of context menu items is quite strange - what you've described is in .Net 4.0 and if you target yor app to .Net 3.5 for instance you will notice the opposite behaviour - you can't make the menu stay opened if the single item is disabled, it just disappears immediately after it shows on right click.
However I think the preferrable way to manage the enabled state of a context menu item (and also the OnClick action it should do) is by a Command.
First you should specify a datacontext for your view, let's say it is the class ViewModel.cs. Then create a command by implementing the ICommand interface, something like this:
public class MyCommand : ICommand
And then you have a CanExecute method which does exactly what it's name says - decides if the command can be executed or not. And the enabled state of the menu item also depends on the return value of this method. So you can keep it returning false as long as you need in and the menu will behave correctly and the menu item will be disabled. Then when you want you can make it return true (by some logic in the method itself) and you'll have again a properly working context menu with enabled menu item.
If you want to disable the entire menu, use the same approach but for the menu.
And if you need to make all the items in the menu disabled (which I think is different from the entire menu), then you can use something like this:
<TabControl Name="MyTabControl" Background="Green">
<TabControl.ContextMenu>
<ContextMenu Name="MyContextMenu" StaysOpen="True" ItemsSource="{Binding Items}">
<ContextMenu.ItemTemplate>
<DataTemplate >
<MenuItem Header="{Binding Header}" IsEnabled="False" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
Here your ItemsSource (Items) is for instance List<MenuItem>, and MenuItem itselw would be your Model (thinking in MVVM) for each of your menu items, which should have property Header that contains the header you want to see in each menu item. Instead of setting the IsEnabled to false as in my dummy example, you can bind it to a property in order to have control on when it must be true and false.