Add control to main UI container - c#

I have few expanders in one XAML. There is another main UI application which contains StackPanel.
LocalControl.XAML
<UserControl>
<Expander Name="ExpA">
<StackPanel>
<ComboBox />
<TextBox />
</StackPanel>
</Expander>
<Expander Name="ExpB">
<StackPanel>
<ComboBox />
<TextBox />
<Button />
<StackPanel>
</Expander>
</UserControl>
I set DataContext for this Expanders and other UI controls' binding in LocalControl.xaml.cs during initialization. Whenever some event triggers, I want to load these expanders in maincontrol under StackPanel.
I have middle man class that listens for the load and unload events from LocalControl.xaml.cs. Lets call it Manager.cs.
Manager.cs
public class Manager {
public Manager(MainControl mc)
{
LocalControl lc = new LocalControl();
lc.loadMe += loadControl;
}
private void loadControl(sender, MyEventArgs e)
{
foreach(var exp in e.Expanders){
mc.SP.Add(exp);
}
}
}
I have main control where I need to display UI controls in my application.
<UserControl>
<StackPanel Name="SP">
<!-- Add expanders here -->
</StackPanel>
</UserControl>
How can I make sure that I have loaded this expander in MainControl StackPanel?
Is there anyway I can expose some Property in LocalControl and I use that property in MainControl on demand?
In a nutshell, I want to load small controls under one XAML file into StackPanel control in another XAML file during run-time. I don't want to add these controls in MainControl XAML during compile time. In my application, I have many expanders that need to be added run time.
I hope the question is clear.
P.S: Please disregard all syntaxing issues. My intention is to give some idea what I want to do with this.

Related

How to get key strockes on parent control to reach child controls that host the actual binding but are not focused

Please let me know if I need to clarify or post more code!
I could find quite some resources on how to pass keystrokes from a child element to a parent element but not the way around.
I explain my use case:
I have a parent Window (let's call it wndSession) with a couple of focusable controls and a ContentControl which gets its content (a UserControl) assigned using DataTemplate bindings.
<Window.Resources>
<DataTemplate DataType="{x:Type vm:vmLearnModule1}">
<local:viewLearnModule1 />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnModule2}">
<local:viewLearnModule2 />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnModule3}">
<local:viewLearnModule3 />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnResults}">
<local:viewLearnResults />
</DataTemplate>
</Window.Resources>
And:
<ContentControl Content = "{Binding learningViewModel}"
Name = "contentControl"
Grid.Row = "1"
Grid.Column = "1"
Height = "300"
Margin = "20, 0, 0, 0" />
Now each UserControl may have different KeyBinginds assigned, and as long as the focus is on the UserControl all works fine.
Example of bindings on a UserControl:
<UserControl.InputBindings>
<KeyBinding Key="F1" Command="{Binding cmdResultNotKnown}"/>
<KeyBinding Key="F2" Command="{Binding cmdResultAlmostKnown}"/>
<KeyBinding Key="F3" Command="{Binding cmdResultKnown}"/>
</UserControl.InputBindings>
My commands are defined all like this sample:
private ICommand _cmdTest;
/// <summary>
/// Test
/// </summary>
public ICommand cmdTest => this._cmdTest ??= new RelayCommand(param => this.doTest(), param => this.canTest);
private bool canTest => true;
private void doTest()
{
// Some code here
}
Now to the issue:
If I click on any focusable control on the parent window wndSession, the UserControl or its currently focused child will lose Focus and a control of the parent (wndSession) will gain focus (ok, I'd expect that so far).
But because of this, the UserControl will not react to KeyBindings anymore unless I click on any control of the UserControl first. That is not a really good UX...
What I need is a way to:
First i thought about something like to "bubble down" the keystroke to the child controls of wndSession, and especially to the UserControl in the Contentcontrol, in the event that wndSession does not already address the KeyBinding.
But as remarked that would cause many issues. So in order to avoid conflicts (which control can now use it's bindings, etc.) I would suppose that what is needed is to pass the keystroke to a specified target, which in my case would be the UserControl inside the ContentControl if wndSession could not handle the key as no binding is specified.
In short: have wndSession operate as a Router passing all unknown keystrokes (not registered by a KeyBinding) to a Gateway/Proxy (ContentControl/UserControl)
Then there will be only a path down the tree with little risk of conflicts. Currently I fail simulating a keystroke on the target control from parent, so that its KeyBinding can fire.

How to prevent a UserControl from taking focus away from its parent?

I am using MVVM for my application and have a form that allows the user to enter basic personnel information. The form includes a UserControl which is, basically, an ItemsControl that includes textBoxes that can be dynamically created. This is a simplified version:
<ItemsControl x:Name="items" ItemsSource="{Binding MyItemsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="row">
<TextBox x:Name="textBox" Text="{Binding ContactInfo, ValidatesOnExceptions=True}" extensions:FocusExtension.IsFocused="{Binding IsFocused}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="NewItemButton" Command="{Binding AddItemToMyCollectionCommand}" />
I want the TextBox that has just been created to receive focus, therefore I added an attached property. This is part of it:
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
In the form that contains the UserControl there are several other text boxes before and after. The UserControl has its own ViewModel, which I set as the DataContext of the control through a property in the container's ViewModel. Basically, a simplified version of the container looks like this:
<StackPanel Orientation="Horizontal" />
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
My problem is that I want the firstName TextBox to get the focus when the form is loaded for the first time, but the form keeps on placing the focus on the first TextBox of the phones UserControl. I tried to override it by using firstName.Focus() on the Loaded event of the form, but this didn't work, and no matter what I tried the focus is still on the phones userControl instead of the first element in the form that contains it.
Does anybody have any idea how to solve this?
Thanks.
Here you go
add FocusManager.FocusedElement="{Binding ElementName=firstName}" to your stack panel
<StackPanel Orientation="Horizontal"
FocusManager.FocusedElement="{Binding ElementName=firstName}"/>
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
also notice that you may need to prevent items control in the user control from focusing itself
<ItemsControl x:Name="items" Focusable="False" >
<ItemsControl.ItemTemplate>
I guess I managed to find a solution. The problem was that the form I created was itself a user control inside a window, and never got focus. I didn't think that would be relevant so I didn't mention it in my previous post- sorry. I found in this solution for forcing focus to a user control.
Basically, when I have a UserControl inside a window it doesn't get focus even if I try to set the focus with either Focus() or FocusedElement. So to overcome this problem I found on a different post a workaround. Basically I added it to the code-behind of the UserControl that contains the firstName TextBox. If we call the UserControl, say, PersonalInfoUserControl, the constructor of the control would look like this:
public PersonalInfoUserControl()
{
InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(UserControl_IsVisibleChanged);
}
I added an event handler to the IsVisibleChanged event of the control. The method would look like this:
void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
firstName.Focus();
}));
}
}

Using Icons in WPF Database Driven Application Results

I am attempting to make a WPF application. The application needs to use a "list view" to show results of queries to the database. I have been able to successfully create the application (GUI, database, LINQ, etc.), however, the display of my query results appear more "gridlike".
The specifications for the project below show that each record that appears in the results needs to have a green circle icon next to it. I have removed the actual results from the images below to keep the contents of the database private.
I don't have enough Reputation Points to post images, so I posted pictures so a sample/testing domain that I use. You can see screenshots here of the WPF app and code here:
http://digitalworkzone.com/WPF.html
What am I doing incorrectly? Is there something I need to add or modify to my code to be able to get the green circles and more of a "list" style to display my query results?
Understand the WPF content model. http://msdn.microsoft.com/en-us/library/bb613548.aspx
Anything that has a 'Content' property basically behaves in two ways. If the 'Content' is set to something that derives from UIElement, then the class will manage it's own presentation. Anything else, however, will just get .ToString() called, and it's text displayed instead.
What this means in the long run is that everything in WPF can display anything. If you want to show a button in a button, you can. For example:
<Button>
<Button.Content>
<Button Content="This will show as text" />
</Button.Content>
</Button>
The inner button will have text, but the outer button will show a Button because Button derives from UIElement and therefore will handle its own presentation.
In your picture examples above, you have ListBoxes/DataGrids that you want to fill in with graphical information. Try this out:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<Button Content="One"/>
<Button Content="Two"/>
<Button Content="Three"/>
<Button Content="Four"/>
</ListBox.Items>
</ListBox>
Now you have a ListBox that shows Buttons instead of Text. You can take this a step further and contain the items in a stackpanel, for example:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
</ListBox.Items>
</ListBox>
Now we have items that contain a layout container (StackPanels, which then contains other elements).
However, if you set the ItemsSource elsewhere, you can actually use a DataTemplate to display the contents. A DataTemplate in effect targets a particular class and lays out it's contents as defined in XAML. Consider:
Code Behind:
public partial class MyWindow : UserControl {
public MyWindow() {
InitializeComponent();
MyListBox.ItemsSource = new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White"),
};
}
XAML:
<ListBox HorizontalContentAlignment="Stretch" x:Name="MyListBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when the Listbox displays, it will cycle through each of the items in the ItemsSource property, and then lay them out using the DataTemplate. It's possible to have the DataTemplate target specific classes by using the DataType property if you're using polymorphism (as in different types of people such as 'Cusomters' or 'Employees' which all derive from 'Person).
The problem with this approach is that you are setting the value of the items directly, which is bad form. It's better to define a class that handles all of the data for your view separately. Consider:
public class ViewModel {
// WPF will automatically read these properties using reflection.
public List<Person> People {
get {
return new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White")
};
}
}
}
That will hold all the data for the view, now let's add it to the actual window. First we need to reference the namespace ('xmlns' means xml namespace):
<Window x:Class="Sharp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lol="clr-namespace:Sharp">
The namespace is Sharp (the namespace where my stuff lives), and the alias we'll give it is lol. Now we attach our ViewModel class to the window by setting it to the DataContext property, as in:
<Window>
<Window.DataContext>
<lol:ViewModel />
</Window.DataContext>
</Window>
This makes all of the public properties on the ViewModel class available to the Window. This way, if we want to read the Persons information into our ListBox, we simply say:
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding People}" >
...
</ListBox>
Notice that we say ItemsSource={Binding People}, which means 'scan the ViewModel for any public properties called 'People' and then retrieve those results. This is essentially the fundamentals behind the MVVM approach. You might have all of your business logic in one or many classes which handle the main application operation in a Model, but then you have a ViewModel which interacts with the Model and exposes the results as public properties. WPF automatically binds to those properties and presents them for your. The information just flows, rather than setting the values by force.
To really understand how WPF is supposed to work, you should take some time to understand the basics of MVVM. WPF was really designed with MVVM in mind, and so to really get how WPF is supposed to work, you really should take the time to get your head around it. Take a look at:
http://agilewarrior.wordpress.com/2011/01/11/simple-mvvm-walkthrough-part-i/ .
<ListBox ItemsSource="{Binding QueryResults}">
<ListBox.ItemsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}"/>
<TextBlock Text="{Binding TextSource}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemsTemplate>
</ListBox>
Will work if you have a list of objects named QueryResults in your code behind. Each object needs to have an string property named ImageSource and a string property named TextSource.
However, since you only need to display a green circle icon for each of the items, you can hardcode the image source. The above will work if you want to have a different icon for each, though.
Also note that in order for this to work, you need to set the DataContext of the window to DataContext="{Binding RelativeSource={RelativeSource Self}}"

Bind button in DataTemplate to command in the form's ViewModel

My problem is similar to the one described in this question:
WPF MVVM Button Control Binding in DataTemplate
Here is my XAML:
<Window x:Class="MissileSharp.Launcher.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MissileSharp Launcher" Height="350" Width="525">
<Grid>
<!-- when I put the button here (outside the list), the binding works -->
<!--<Button Content="test" Command="{Binding Path=FireCommand}" />-->
<ListBox ItemsSource="{Binding CommandSets}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- I need the button here (inside the list), and here the binding does NOT work -->
<Button Content="{Binding}" Command="{Binding Path=FireCommand}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
It's just a ListBox, bound to an ObservableCollection<string> named CommandSets (which is in the ViewModel).
This binding works (it displays a button for each item in the collection).
Now I want to bind the button to a command (FireCommand), which is also in the ViewModel.
Here's the relevant part of the ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand FireCommand { get; set; }
public ObservableCollection<string> CommandSets { get; set; }
public MainWindowViewModel()
{
this.FireCommand = new RelayCommand(new Action<object>(this.FireMissile));
}
private void FireMissile(Object obj)
{
System.Windows.MessageBox.Show("fire");
}
}
The binding of this button does NOT work.
From what I've understood from the question I linked above, the binding doesn't work because:
(correct me if I'm wrong)
The button is inside the ListBox, so it only "knows" the binding of the ListBox (the ObservableCollection, in this case), but not the binding of the main window
I'm trying to bind to a command in the main ViewModel of the main window (which the button doesn't "know")
The command itself is definitely correct, because when I put the button outside the ListBox (see the XAML above for an example), the binding works and the command is executed.
Apparently, I "just" need to tell the button to bind to the main ViewModel of the form.
But I'm not able to figure out the right XAML syntax.
I tried several approaches that I found after some googling, but none of them worked for me:
<Button Content="{Binding}" Command="{Binding RelativeSource={RelativeSource Window}, Path=DataContext.FireCommand}" />
<Button Content="{Binding}" Command="{Binding Path=FireCommand, Source={StaticResource MainWindow}}" />
<Button Content="{Binding}" Command="{Binding Path=FireCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
Could someone please:
give me the proper XAML to bind the button inside the ListBox to a command in the form's MainViewModel?
point me to a link where this advanced binding stuff is explained in a way that a WPF/MVVM beginner can understand?
I'm feeling like I'm just copying and pasting arcane XAML incantations, and so far I don't have any clue (and can't find any good documentation) how I would figure out by myself in which cases I'd need RelativeSource or StaticResource or whatever instead of a "normal" binding.
It's:
{Binding DataContext.FireCommand,
RelativeSource={RelativeSource AncestorType=ListBox}}
No need to walk up to the root unless you actually change the DataContext along the way, but as the ListBox seems to bind to a property on the main VM this should be enough.
The only thing i recommend reading is the Data Binding Overview, and the Binding class documentation (including its properties).
Also here is a short explanation on how bindings are constructed: A binding consists of a source and a Path relative to that source, by default the source is the current DataContext. Sources that can be set explicitly are: Source, ElementName & RelativeSource. Setting any of those will override the DataContext as source.
So if you use a source like RelativeSource and want to access something in the DataContext on that level the DataContext needs to appear in the Path.
This may be considered unrelated by most, but this search is only 1 of 3 results that you'll find searching for data binding commands to controls inside a data template--as it relates to Xamarin Forms. So, maybe it'll help someone now-a-days.
Like me you may wonder how to bind commands inside a BindableLayout. Credit jesulink2514 for answering this at Xamarin Forums, where it's probably overlooked by many because of all the comments. Here's his solution, but I'm including the link below:
<ContenPage x:Name="MainPage">
<ListView Grid.Row="1"
ItemsSource="{Binding Customers}"
VerticalOptions="Fill"
x:Name="ListviewCustomer">
<ListView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Property}"/>
<Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}"
CommandParameter="{Binding .}">Click me</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
https://forums.xamarin.com/discussion/comment/217355/#Comment_217355

How do you wire up views in Silverlight and MVVM using a TreeView?

I am building a Silverlight app which comprises a TreeView of menu options in a lefthand column and a ContentView in a righthand column. The idea is that the SelectedItemChanged event of the TreeView will change the view in the content area.
What is the 'purest MVVM' way of achieving this?
My idea is to have a TreeMenuView and TreeMenuViewModel for managing the menu events, but after that I'm a bit lost. I could use an EventAggregator to send a message from the TreeMenuViewModel to a `ContentViewModel' that would then set its current ContentView based on the message args- but surely that breaks MVVM, in the sense that a ViewModel shouldn't know about UI constructs like a View?
Am I missing something simple here?
How does a ViewModel layer drive the View selection?
I would create a ShellViewModel which had:
ObservableCollection<ViewModelBase> AvailablePages
int SelectedPageIndex
ViewModelBase CurrentPage, which returns AvailablePages[SelectedPageIndex]
Your ShellView can be anything you want. If you want to display your AvailablePages in a TreeView, then go ahead. Just remember to bind SelectedIndex to `SelectedPageIndex
In your case, I would create a DockPanel with a TreeView on the Left bound to AvailablePages, and a ContentControl on the right with ContentControl.Content bound to CurrentPage
Edit
Here's an example
<DockPanel>
<TreeView DockPanel.Dock="Right"
ItemsSource="{Binding AvailablePages}"
SelectedItem="{Binding SelectedPageIndex}">
...
</TreeView>
<ContentControl Content="{Binding CurrentPage}" />
</DockPanel>
Then use DataTemplates to define how the ContentControl containing CurrentPage will look
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomePageViewModel}" />
<local:HomePageView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:CustomerViewModel}" />
<local:CustomerView />
</DataTemplate>
</Window.Resources>
Ok I give it a shot
in TreeMenuViewModel:
public string PropSelectedItem
{
get;
set;
}
in TreeMenuView:
<TreeView Context="{Binding TreeMenuViewModel}" Content="{Binding PropSelectedItem, Mode=OneWayToSource}"/>
in ContentViewModel:
public ViewModelBase PropSelectedItem
{
get
{
switch(TreeMenuViewModelStatic.PropSelectedItem)
{
case "Booo": return typeof(View1);
case "Foo": return typeof(View2);
}
}
private set;
}
in ContentView:
<ContentControl Context="{Binding TreeMenuViewModel}" Content="{Binding PropSelectedItem, Mode=OneWay}"/>
and you need a value convertor here

Categories