WPF UserControl.Resources reference - c#

I am try to start learning WPF and i am using Telerik.
So i start with simple ComboBox in this article and this is my control:
<telerik:RadComboBox Height="20" Width="200" ItemsSource="{Binding Source={StaticResource DataSource}, Path=Agency}"></telerik:RadComboBox>
What i am trying to do now is to bind an object but first to declare a resource in the XAML (from the article):
<UserControl.Resources>
<example:AgencyViewModel x:Key="DataSource"/> // AgencyViewModel is a class
</UserControl.Resources>
So my problem is that after UserControl i don't have the option Resources, i try to put it inside my control, so i be happy to understand how this is working in WPF

You have to set the DataContext dependency property on a parent control in relation to your ComboBox. The DataContext is then inherited by all (logical-)children. You can then bind to properties on the object referenced by the DataContext dependency property. You do that by referencing the x:Key of your resource with a StaticResource Markup Extension construct.
<UserControl>
<UserControl.Resources>
<example:AgencyViewModel x:Key="DataSource"/> // AgencyViewModel is a class
</UserControl.Resources>
<Grid DataContext="{StaticResource DataSource}">
<telerik:RadComboBox Height="20" Width="200"
ItemsSource="{Binding ItemsCollectionDefinedInViewModel}" />
</Grid>
</UserControl>
You can also do it as it is done in the article without setting the DataContext but instead setting the Source of the binding explicilty.
ItemsSource="{Binding Source={StaticResource DataSource}, Path=Agency}"

Related

Im creating ViewModels from MainViewModel, how can i connect View to my ViewModel [duplicate]

When I define a DataTemplate inline, Visual Studio knows about the type I'm binding to, and properties in that type come up in autocomplete (for example in the code below I was able to select DisplayName from the autocomplete list inside the FirstViewModel template).
<DataTemplate DataType="{x:Type viewmodels:FirstViewModel}">
<StackPanel >
<Label Content="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
<views:SecondView/>
</DataTemplate>
However, when the data template references an external control, as for SecondViewModel in the code above, when I'm in the file for the SecondView usercontrol, since it's just a control, the type isn't bound and the editor doesn't help me with anything.
I've tried wrapping my whole control (inside the UserControl element) in the same DataTemplate tag, but then my whole view just shows "System.Windows.DataTemplate".
<UserControl x:Class="Gui.Views.Tabs.ExamsTabViews.ExamInfoView"
xmlns:vm="clr-namespace:Gui.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<DataTemplate DataType="vm:ExamInfoViewModel">
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<!-- contents of the template -->
</DockPanel>
</DataTemplate>
</UserControl>
Is there a way to achieve this kind of binding for the editor?
<DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
<views:SecondView/>
</DataTemplate>
when this DataTemplate is instantiated, there will be created SecondView and that SecondView will have a SecondViewModel in DataContext. So there is no need any DataTemplate in SecondViewModel control - bind to DataContext instead ({Binding SecondViewModelProperty}). To have design-time support for such binding use d:DataContext="{d:DesignInstance}:
<UserControl d:DataContext="{d:DesignInstance Type=vm:ExamInfoViewModel,
IsDesignTimeCreatable=True}" ...>

Set a ContextMenu UserControl for a Grid

I have created a custom context menu in a separate user Control Class CustomContextMenuUc.
The simple version of the code looks like this.
<ContextMenu x:Class="CustomContextMenu.CustomContextMenuUc"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ItemsSource="{Binding Groups}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Description.Value}" />
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.OverridesDefaultStyle>True</ContextMenu.OverridesDefaultStyle>
<ContextMenu.Placement>Custom</ContextMenu.Placement>
</ContextMenu>
I want to use this CustomContextMenuUc in multiple Windows. I am able to assign this User control in C# code like
ContextMenuGrid.ContextMenu = new CustomContextMenuUc();
Where ContextMenuGrid is a grid.
I want to do this in XAML How should I do it?
<DataGrid ContextMenu="">
You will have to define the Resource in resources of your window like
<local:CustomContextMenuUc x:Key="MyContextMenu"/>
here local is the xmlns where your contextmenu is defined.
Then you can do
<DataGrid ContextMenu="{StaticResource MyContextMenu}">
Thanks

binding to dynamic resource in ItemsControl on the ItemsSource Property

Hey I was hoping someone could answer a couple questions for me. How am I supposed to ensure that the bound data to itemsource updates dynamically? I can't change bindingsource from staticresource to dynamic resource because the Source Property of the Object Binding is not a dependency property of a dependency object.
What does binding to a staticresource mean exactly? I would think that binding to a dynamicresource would mean that the dependencyproperty updates when the resource changes.
Does binding to a static resource merely attach the resource's initial value?
My goal is just to have signal_viewer update based on the signal_data.
<UserControl.Resources>
<wpfExp:SignalData x:Key="signal_data" />
</UserControl.Resources>
<DockPanel x:Name ="maindockpanel" Height ="Auto" Width ="Auto" LastChildFill="True">
<ToolBarTray DockPanel.Dock="Top">
<ToolBar HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Button Name="load_button" Height="20" Width="Auto" Click="Load_Button_Click">Load</Button>
<Button Name="zoom_in_button" Click="zoom_in_button_Click">Zoom In</Button>
<Button Name="zoom_out_button" Click="zoom_out_button_Click">Zoom Out</Button>
</ToolBar>
</ToolBarTray>
<ItemsControl x:Name ="Signalviewer_Control" ItemsSource="{Binding Source = {StaticResource signal_data}, Path = list_of_signals}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<wpfExp:SignalViewer Signal="{Binding}" MainText="{Binding Path = SignalName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I'm all against putting the ViewModel or data as a Resource in XAML due to all these issues you mentioned.
Instead, either assign the DataContext in code behind:
public SomeWindow() //Window Constructor
{
DataContext = new SomeViewModel();
}
or use ViewModelLocator
or use the RegisterDataTemplate approach outlined here.
Anyways, if you want to resolve this quickly, change your list_of_signals from List<T> to an ObservableCollection<T>

Navigate properly between tab in a WPF MVVM application

I'm doing a WPF application with the M-V-VM patern (i'm using galasoft if it's relevant), but I have issues when I navigate through a tabcontrol.
I'm adding tabs on the run. All the binding seems to goes well : inside the tab or in the header of the tab.
I've bind my tabcontrol to a observable list. Through an interface I'm adding several types of viewmodel to this list and the binding seems correct.
My XAML code looks like this :
<Grid>
<Grid.Resources>
<DataTemplate x:Key="itemTemplate">
<TechnicalControls:ItemTab />
</DataTemplate>
</Grid.Resources>
<TabControl Grid.Row="1" x:Name="MainTab" Grid.Column="1"
ItemsSource="{Binding TabViewModels}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
ItemContainerStyleSelector="{StaticResource LastItemStyleSelector}"
ItemTemplate="{StaticResource itemTemplate}"
>
<TabControl.Resources>
<DataTemplate DataType="{x:Type VM:JobViewModel}" x:Shared="False" >
<FunctionnalControls:Job />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:ExcelJobViewModel}" x:Shared="False">
<FunctionnalControls:ExcelJob />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:MonitoringViewModel}" x:Shared="False">
<FunctionnalControls:Monitoring />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:ErrorViewModel}" x:Shared="False">
<FunctionnalControls:Error />
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
For example if I go from a ExcelJob to another ExcelJob usercontrol, the new usercontrol is not load properly but it changes then it works, for exemple, I can go to a ExcelJob to another ExcelJob if only I go through the monitoring.
I've already look to this this but it didn't work for me.
I've also looked at this : it says that we should not used inputs because you can focus them. I've tried to set the IsEnabled property on the users controls to false. I did it when tabs were changing. It didn't work...
The only solution that I can see is to go through another a new usercontrol with no other purpose to be used every time a tab is changed but this is ugly, and I'm pretty sure, Microsoft thought about this and came up with a better solution.
If necessary I can put the code of the view model.
EDIT : Just to clarify, when I click on other tab with the same control, instead of showing me the new usercontrol, it shows me the previous one. In order to see the new one, I have to change to another tab with another usercontrol then come back on the one I want to see.
I've look through debug and the when I click on the other tab It doesn't call the viewmodel
<UserControl x:Class="App.ExcelJob"
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"
DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}">
<Grid >
<Label>Futur Excel Job</Label>
<TextBox Width="200" Height="60" Text="{Binding Header}"/>
</Grid>
</UserControl>
So Main returns the Mainviewmodel and Main.ExcelJobVM returns the good viewmodel of the usercontrol. the returned isntance is based on selected Index.
The only thing I need is to force the redrawing of the usercontrol or recall the method to update the datacontext, by loading the good viewmodel. I tried, I've failed so far. I'm not sure of what I'm doing because I want to use the event SelectionChanged of the tabcontrol but it would be in the code behind, and I don't know if it would still respect the MVVM pattern.
The problem is that you have the DataContext hardcoded in your UserControl, and your UserControl is a Template.
When you switch to a tab that uses the same Template, WPF doesn't bother to redraw the template and only changes the DataContext behind the template. But in your case, you have the DataContext hardcoded in the UserControl, so it's not using the existing data context from the TabItem.
The best solution would be to remove the DataContext binding from your UserControl, and let it be inherited from the TabItem when the selected item changes.
For example:
WPF says
User has selected ExcelJobA for display. Because of the DataTemplate, let me draw it using an ExcelJob UserControl
<TabItem>
<ContentPresenter DataContext="ExcelJobA">
<local:ExcelJob DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}" />
</ContentPresenter>
</TabItem>
So an ExcelJob UserControl gets created, and by default the DataContext of ExcelJobA would be inherited by the UserControl.
When the user changes the selected tab to ExcelJobB, WPF goes
Hey, the user has changed to ExcelJobB. Because of the DataTemplate, let me draw it using an ExcelJob UserControl, BUT WAIT! I'm already displaying an ExcelJob UserControl, so let me just change the DataContext behind that to ExcelJobB
<TabItem>
<ContentPresenter DataContext="ExcelJobB">
<local:ExcelJob DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}" />
</ContentPresenter>
</TabItem>
So the actual displayed ExcelJob UserControl does not get recreated or redrawn, but only the DataContext behind it changes.
HOWEVER, because you have hard-coded the DataContext inside your UserControl, the actual data context obtained from the selected item doesn't ever get used, because a DataContext specified from inside a <Tag> always takes precedence over a DataContext that would be inherited from further up the visual tree.
You need to remove the DataContext binding from inside your UserControl, and let it get passed in normally from your TabControl's SelectedItem, and it will work fine.
<TabItem>
<ContentPresenter DataContext="ExcelJobA">
<local:ExcelJob /> <!-- DataContext inherited from the ContentPresenter -->
</ContentPresenter>
</TabItem>

Access my Views from bound ItemsControl

I have a typical MVVM scenario: I have an ItemsControl bound to an ObservableCollection of StepsViewModels. I define a DataTemplate so that StepViewModels are rendered as StepViews. The same happens in the StepView: I have an ItemsControl to an ObservableCollection of ParameterViewModels with a DataTemplate to render them as ParameterViews.
My problem is: I have to refresh the ItemsControl to render items added and remove items. I can refresh the ItemsControl with StepViews, because I have access to it and can call ItemsControl.Items.Refresh(). But how can I access the StepViews so I can call a Refresh method? ItemsControl Items are StepViewModels...
Here is the code:
<UserControl x:Class="mylib.EditorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:mylib">
<UserControl.Resources>
<DataTemplate DataType="{x:Type my:StepViewModel}">
<my:StepView HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="27"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="1" VerticalAlignment="Top">
<StackPanel Orientation="Vertical">
<TextBlock Name="lblHelpRefresh" Margin="0 7 0 7" Visibility="{Binding HelpVisible}"
VerticalAlignment="Center" HorizontalAlignment="Center"
FontSize="9pt" Foreground="#949494"
Text="Please click refresh to reload this list"/>
<ItemsControl Name="stkStepContent"
ItemsSource="{Binding Steps}"/>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
How can I access the StepView controls that render the stkStepContent Items?
if you have an ObservableCollection then the adding and removing of items should refresh automatically. But if you just updated a property in your StepsViewModel then, StepsViewModel should be the one to alert the update either by implementing the INotifyPropertyChanged interface or by inheriting from the DependencyObject and using Dependency properties instead.
Anyway, if you really want to have the ability to manually refresh your collection, use a CollectionViewSource in your ViewModel. Then call the View.Refresh() method of your CollectionViewSource.
To access your views in the codebehind you can use the DataContext of the control and cast it into the appropriate view. For example:
ObservableCollection<StepViewModel> vmCollection =
stkStepContent.DataContext as ObservableCollection<StepViewModel>;
foreach(StepViewModel vm in vmCollection)
{
vm.Refresh();
}
EDIT: Does the StepViewModel contain a second ObservableCollection with items that you are trying to refresh? If so, you might be able to wireup a PropertyChanged event so that when the internal ObservableCollection gets updated it triggers a PropertyChanged notification on the StepViewModel

Categories