Adding and changing usercontrol at run time in a WPF application - wpf

I have a situation as follows, i am going to use WPF for first time, so any suggestion abt how to proceed whould be great:
I hav a drop down, when i select any item from it - it should change the structure of controls in same window. New controls contain - two menu items and a text box and a list box. Selecting one menuitem will display text box and other will show list box. Now for each item in initial combo box i will have different info for the each menu items.
Problems:
Say i have 10 items in combo box - and 2 menu items for each - so 20 different stuff to show.
-- How should i declare these 20 different stuffs
-- How should i load each when a particular combination is selected

You should look at ControlTemplate. You can define a set of templates, then apply them to a control causing them to be whatever you want them to be. so, when the item changed event fires on your dropdown, load and apply the template that you want.
<!--- your xaml file --->
<Control x:Name="Main"/>
// you CS file....
OnItemChanage(....)
{
if ( Main!= null )
Main.Template = MyNewTemplate;
}

If you want to show multiple sets of controls at once, dd all of the controls to your window and set their Visibility using a data binding, and use the ComboBox to update the propertie the controls are bound to.
Or if you only want to show one control at a time, just use a DataContext from the ComboBox:
<Window.DataContext>
<x:Array x:Key="myItems">
<local:Item MenuItem1="abc" MenuItem2="def" />
<local:Item MenuItem1="ghi" MenuItem2="jkl" />
...
<local:Item MenuItem1="ghi" MenuItem2="jkl" />
</x:Array>
</Window.DataContext>
<Grid>
...
<ComboBox x:Name="selection" ItemsSource="{Binding}">
...
<StackPanel DataContext="{Binding /}" ...>
<MenuItem Header="{Binding MenuItem1}" OnClick="DisplayListBox" />
<MenuItem Header="{Binding MenuItem2}" OnClick="DisplayTextBox" />
<TextBox Visibility="Hidden" ... />
<ListBox Visibility="Hidden" ... />
</StackPanel>
</Grid>
with appropriate code for DisplayListBox and DisplayTextBox

Related

Expander-like WPF control that only hides empty children

In my WPF view, I need something similar to an Expander or a TreeView, but instead of completely hiding the content, I only want to hide empty parts, i.e. TextBoxes with null or empty text, or empty ItemCollections.
I thought about using a style with a DataTrigger or set Visibility with a converter, but how would I link that to the parent's setting (e.g. IsExpanded)?
I would like to avoid doing this in the ViewModel, as that would need a property for each section (and I need lots of them), but it's purely visual and therefore IMHO it only belongs to the View.
So I guess the way to go is to use DependencyProperties or write some CustomControls, but I don't have an idea where to start. The XAML of the end result could look something like this:
<CustomExpander Header="Main" CollapseContentIfEmpty="True">
<CustomExpander Header="Section1" CollapseContentIfEmpty="True">
<StackPanel>
<TextBox Text="{Binding SomeString}" />
<TextBox Text="{Binding SomeEmptyString}" />
</StackPanel>
</CustomExpander>
<CustomExpander Header="Section2" CollapseContentIfEmpty="True">
<ListView ItemsSource="{Binding SomeCollectionView}" />
</CustomExpander>
</CustomExpander>
In this example, if CollapseContentIfEmpty is set to true and the CollectionView shows no elements (e.g. due to filters), only the content of SomeString should be visible, along with all the headers. If SomeString is empty, only "Main" should be visible, as now all child CustomExpanders are empty as well.
Setting CollapseContentIfEmpty to false (e.g. via a Button like in Expander) would show all Children again, regardless if they are empty or not.
I thought about using a style with a DataTrigger or set Visibility with a converter, but how would I link that to the parent's setting (e.g. IsExpanded)?
Use a binding with a {RelativeSource}.
In the following example, the TextBlock is invisible unless you set the Tag property of the parent UserControl to true:
<UserControl xmlns:sys="clr-namespace:System;assembly=mscorlib">
<UserControl.Tag>
<sys:Boolean>false</sys:Boolean>
</UserControl.Tag>
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel>
<TextBlock Text="text..."
Visibility="{Binding Tag,
RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</UserControl>
You can of course replace the UserControl with a custom control with a custom bool property.
An Expander collapses its entire Content which is different from hiding specific controls in that content.

Place a ContentControl inside a custom control XAML

I created a custom control and what to use a ContentControl inside that control for use of the MVVM design pattern however my control does not like this when I run the application. For testing I also tried other standard controls but none of them work inside the custom control... just more custom controls that are dependant on the parent custom control.
Does anybody suggest how to place standard controls such as the ContentControl inside a custom control?
Cheers.
EDIT
XamlParseException -> 'Add value to collection of type
'System.Collections.ObjectModel.ObservableCollection(Ribbon_Framework.RibbonTabItem)'
threw an exception.' Line number '8' and line position '14'.
<Ribbon:Ribbon AutomaticStateManagement="True" x:Name="Ribbon">
<ContentControl x:Name="SearchRibbon" Content="{Binding Path=SearchRibbon}" ContentTemplate="{DynamicResource SearchRibbonTemplate}" />
</Ribbon:Ribbon>
Inside the contentcontrol ->
<DataTemplate x:Key="SearchRibbonTemplate">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ribbon:RibbonTabItem Header="Search">
<Ribbon:RibbonGroupBox Header="{Binding Path=DisplayName}" Width="100">
<Ribbon:Button Width="100" Icon="{Binding Path=TemplateResource}" LargeIcon="{Binding Path=TemplateResource}" Command="{Binding Path=Commands}" />
</Ribbon:RibbonGroupBox>
</Ribbon:RibbonTabItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Your Ribbon control expects an object of RibbonTabItem type (since it houses an
ObservableCollection<RibbonTabItem>
so you can only add the RibbonTabItem class to it - you need to make sure your control allows other elements inside it. Some 3rd party controls get round this by providing a content control inside the inner item of their custom control (i.e. let RibbonTabItem have a ContentControl inside it) or allow you to customise the item template
You need to change your implementation of Ribbon or change the functionality of RibbonTabItem for this to work. Look at ItemsControl.Items property and see what type that is. You should try using that type for your ObservableCollection

DisplayStateBehavior not initially applying state transition in listbox item template

I have a listbox item template that contains visual states. I have DisplayStateBehaviors that set the states to either the on/off states. My implementation only 1/2 works. On initial display, the base state is active, regardless of the value in the DataContext. When the data in the DataContext changes, the proper state is activated.
How do I get the proper state displayed on initial display of the listbox.
For security reasons I can not copy/paste XAML or View-Model code.
Edit: While I can't copy/paste the real code, the following skeleton hopefully reproduces the problem.
In a globally visible resource file:
<DataTemplate x:Key="MyObjectItemTemplate">
<Grid>
<VisualStateManager.VisualStateGroups>
... blend goodness ...
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
The data context is passed into the data template by associating it with the ItemsTemplate attribute of the listbox in the main UI.
<ListBox ... ItemTemplate="{DynamicResource MyObjectItemTemplate}" .../>
I found my issue. In the event it helps others, what I did to fix it was to extract out the guts of the DataTemplate into a UserControl. This made everything work as I expected. The DataTemplate still exists and is still involved, but it only has one element - the user control. The visual states are all part of the user control.
In a globally visible resource file:
<DataTemplate x:Key="MyObjectItemTemplate">
<MyNamespace:MyUserControl/>
</DataTemplate>
In a separated file MyUserControl.cs
<UserControl ... x.Class="MyNamespace.MyUserControl" ...>
<Grid>
<VisualStateManager.VisualStateGroups>
... blend goodness ...
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>
The data context is passed into the data template by associating it with the ItemsTemplate attribute of the listbox in the main UI.
<ListBox ... ItemTemplate="{DynamicResource MyObjectItemTemplate}" .../>

How to prevent NewItemPlaceholder row in a ComboBox bound to the same DataTable as a DataGrid in WPF

I'v a wizard style application that uses pages and the user can navigate between them either by Next and Previous buttons or by using a navigation bar to directely access certain pages.
On one page (i'll call it "grid page")i have a DataGrid bound to a DataTable. There is some initial data in the DataTable, but using the DataGrid the user can add, edit and delete rows as he wishes.
On the next page (i'll call i "combo box page") have a ComboBox which is bound to the same DataTable as the DataGrid on the grid page.
Both pages use the same object as data context.
If i jump to the combo box page directly everything works fine, the combo box has an entry for each row in the DataTable like it's supposed to be. Now i navigate to the grid page and don't touch anything there and then go to the combo box page again. Now the ComboBox displays a new item, a NewItemPlaceholder. Obviously this happens because the DataGrid has UserCanAddRows set to true and thus displays a placeholder row for a new item. But this should only concern the DataGrid and not the bound DataTable, so in my eyes this is a bug, or at least an absolutely horrible design.
Of course i don't want the NewItemPlaceholder in my ComboBox (and selecting it causes a lot of problems). So how can i prevent it from being displayed in the ComboBox?
Update: In the meantime i found out the placeholder item isn't in the DataTable as a row, which makes it even stranger, unless there is a flag in a DataTable that says "there is a NewItemPlaceholder in this table" but isn't a row itself. Additionally when i register to the Initialized event of the ComboBox i have the 2 items i'm looking for, when i register to the Loaded event i have the NewItemPlaceholder as well, so it must be added somewhere between those 2 events.
Use a CollectionViewSource. The problem occurs when more than one control shares the same view of a collection such as when a collection is bound in one case to a DataGrid and in another to the ComboBox. If the DataGrid allows the user to add items (i.e., the DataGrid's CanUserAddRows is true), the NewItemPlaceholder may have been added to the view. Try something like
<UserControl >
<UserControl.Resources >
<CollectionViewSource Source="{Binding MyCollection}"
x:Key="SourceWithoutNewItemPlaceholder" />
</UserControl.Resources>
<Grid Name="LayoutGrid" >
<ComboBox ItemsSource={Binding Source={StaticResource SourceWithoutNewItemPlaceholder}} >
</Grid>
</UserControl>
After several failed attempts i found 2 solutions:
1) In the Loaded event of the page i make a binding in the code behind, not to the DataTable itself, but to a DataTable.ToList. If i do it that way the NewItemPlaceholder won't show up.
2) In the view model i make a new property which returns the DataTable.ToList and bind to it. This way i can do the binding in XAML.
Just for reference, methods that don't work: Filter property on the ComboBox throws a NotSupportedException, seems the DataTable doesn't support this. Deleting the NewItemPlaceholder in the Loaded event of the ComboBox doesn't work either because you can't delete items when an ItemsSource is set. Removing the NewPlaceHolderItem when leaving the page with the data grid doesn't work either, because i can't find where it is (it's not in the DataTable or the View of the DataTable)
Use a CollectionViewSource to bind one control(Combobox) and collection property defined in ViewModel to bind another control (Datagrid).
Property in ViewModel :
private List<Rate> _rate = new List<Rate>();
public List<Rate> RatePlans
{
get { return _rate; }
set
{
_rate = value;
OnPropertyChanged("RatePlans");
}
}
Then in XAML :
<UserControl >
<UserControl.Resources >
<CollectionViewSource Source="{Binding RatePlans}" x:Key="RatesSource" />
</UserControl.Resources>
<Grid Name="LayoutGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" DisplayMemberPath="RatePlanName" ItemsSource="{Binding Source={StaticResource RatesSource}}" HorizontalAlignment="Stretch"/>
<DataGrid Grid.Row="1" HorizontalAlignment="Stretch" CanUserAddRows="True" AutoGenerateColumns="False" ItemsSource="{Binding RatePlans}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding RatePlanName}" Header="Rate Plan" />
<DataGridTextColumn Binding="{Binding RoomType}" Header="Room Type" />
<DataGridTextColumn Binding="{Binding Price}" Header="Price"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

Dialog form in wpf change content

I want to display a dialog form for new and edit actions... However title, buttons , and few other things should change.
I am wondering how i could implement this. Provide an enum value at constructor ? Like Mode.New or Mode.Edit ? Is there a way to avoid writing code like spNewButtons.Visibillity=Collapsed .. etc , and put it inside wpf ?
You can bind visibility with your mode property, and create a specific IValueConverter to convert the mode to a proper Visibility value. ie:
<StakPanel Visibility={Binding Mode,Converter={StaticResource myProperConverter}}></StackPanel>
Usually my WPF dialogs are all ContentControls that get displayed in a Popup.
My code usually looks like this:
<Grid Name="RootPanel">
<!-- Other Content -->
<!-- Popup is always last so it gets displayed on top of other contnet -->
<local:PopupPanel
local:PopupPanel.PopupParent="{Binding ElementName=RootPanel}"
local:PopupPanel.IsPopupVisible="{Binding IsPopupVisible}"
local:PopupPanel.PopupEnterKeyCommand="{Binding SaveCommand}"
local:PopupPanel.PopupEscapeKeyCommand="{Binding CancelCommand}">
<DockPanel>
<!-- Header -->
<Label DockPanel.Dock="Top" Content="{Binding PopupHeader}" />
<!-- Buttons -->
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Save" Command="{Binding PopupSaveCommand}" />
<Button Content="Cancel" Command="{Binding PopupCancelCommand}" />
</StackPanel>
<!-- Actual content displayed is determined by DataTemplates -->
<ContentControl Content="{Binding PopupContent}" />
</DockPanel>
</local:PopupPanel>
</Grid>
I removed a lot of the styles to make this easier to read, but you can see the general idea of how it's put together. My ViewModel usually contains properties for IsPopupVisible, PopupContent, and PopupHeader, and commands for PopupSaveCommand and PopupCancelCommand
I use my own custom popup in most cases, although the same thing could be done with a WPF popup.

Resources