I have a TreeView object bound to a DataSet. Inside of the TreeView.ItemTemplate, I am using a HierarchicalDataTemplate containing the controls that I am rendering.
Does anyone know how to change the Visibility property of a control inside of a HierarchicalDataTemplate? I have tried using the BooleanToVisibilityConverter from the .NET framework, but cannot get the binding to work properly.
The boolean variable in my ViewModel named "moveButtonVisibility" is bound to the Visibility property of the button in my XAML. The BooleanToVisibilityConverter then attempts to convert the corresponding boolean value (true/false) to a Visibility value (visible/hidden). "moveButtonVisibility" is not part of the TreeView's ItemSource.
A stripped down version of my code is shown below. I have removed all of the code in my XAML except for the Button control "MoveHereButton" that I want to change the visibility property on:
VIEWMODEL (C#):
private bool _moveButtonVisibility;
public bool moveButtonVisibility
{
get { return _moveButtonVisibility; }
set
{
_moveButtonVisibility = value;
RaiseChange("moveButtonVisibility");
}
}
VIEW (XAML):
<Page>
<Page.Resources>
<BooleanToVisibilityConverter x:Key="visibilityConverter"/>
</Page.Resources>
<Grid HorizontalAlignment="Center" VerticalAlignment="Top">
<TreeView HorizontalAlignment="Center" x:Name="treeView1" VerticalAlignment="Top" ItemsSource="{Binding Path=rsParentChild}" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" >
<TreeView.ItemContainerStyle>
<Style>
<Setter Property="TreeViewItem.IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=rsParentChild, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<Grid Focusable="False" Margin="5,10,5,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Name="MoveHereButton" Content="Move Here" Visibility="{Binding DataContext.moveButtonVisibility, Converter={StaticResource visibilityConverter}}" Click="MoveHereButton_Click" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Page>
The following worked:
<Button Name="MoveHereButton"
Content="Move Here"
Visibility="{Binding DataContext.moveButtonVisibility,
RelativeSource={RelativeSource AncestorType={x:Type Page}},
Converter={StaticResource visibilityConverter}}"
Click="MoveHereButton_Click" />
The key was to add:
RelativeSource={RelativeSource AncestorType={x:Type Page}}
inside of the Visibility binding to force the control to use the DataContext of the Page.
Related
I have a working dynamic context-menu but there seems to be an issue with the XAML-formatting. To me, it seems as the first template selected will be used for all the menu items.
I would like to achieve a result where the icons are placed in the normal standard location on the left highlighted column and I could still customize the right-side XAML-content for each menu item separately as defined in their DataTemplate.
Heres the XAML-code snipped, used with ListBox
<ListBox.ContextMenu>
<ContextMenu ItemsSource="{Binding ModelContextMenu}"
ItemTemplateSelector="{StaticResource ContextMenuItemDataTemplateSelector}">
</ContextMenu>
</ListBox.ContextMenu>
And the data templates for different menu items:
<DataTemplate x:Key="ChangeColorMenuItem" DataType="MenuItem">
<DockPanel>
<MenuItem Header="{Binding DisplayName}"
Command="{Binding Command}">
</MenuItem>
<xctk:ColorPicker
SelectedColor="{Binding DataContext.SelectedColor, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:View}}}"
Margin="0,0,0,0" />
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="NormalMenuItem" DataType="MenuItem">
<MenuItem Header="{Binding DisplayName}"
Command="{Binding Command}">
<MenuItem.Icon>
<Image Source="{Binding Icon}"/>
</MenuItem.Icon>
</MenuItem>
</DataTemplate>
and finally the template selector class:
public class ContextMenuItemDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (container is FrameworkElement element && item is ContextMenuCommand commandItem)
{
switch (commandItem.MenuType)
{
case ContextMenuType.Normal:
return
element.FindResource("NormalMenuItem") as DataTemplate;
case ContextMenuType.ChangeColor:
return
element.FindResource("ChangeColorMenuItem") as DataTemplate;
default:
throw new ArgumentOutOfRangeException();
}
}
return null;
}
}
EDIT 1
Each MenuItem content is capsulated in the list which is bind to the ContextMenu. The objects in the collection do contain all the data for each MenuItem, such as icon, display name, and ICommand.
public ObservableCollection<ContextMenuCommand> ModelContextMenu => _selectedModel.Commands;
EDIT 2
The desired (standard) icon location marked with blue crosses on the following image:
The problem seems to be two inner MenuItem because of the data templates in question.
The problem was solved by defining ItemTemplate and a custom ItemContainerStyle in the following way:
DataTemplates
<DataTemplate x:Key="ChangeColorMenuItem" DataType="MenuItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding DisplayName}"
Padding="5,0,5,0"/>
<xctk:ColorPicker Grid.Column="1"
SelectedColor="{Binding DataContext.SelectedEntityColor, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:PanelView}}}"
Margin="0,0,0,0" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="NormalMenuItem" DataType="MenuItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding DisplayName}" />
</Grid>
</DataTemplate>
ListBox ContextMenu
<ListBox.ContextMenu>
<ContextMenu ItemsSource="{Binding CadModelContextMenu}"
ItemTemplateSelector="{StaticResource PanelContextMenuItemDataTemplateSelector}"
UsesItemContainerTemplate="True">
<ContextMenu.Resources>
<ResourceDictionary>
<Image x:Key="menuIcon" x:Shared="false"
Source="{Binding Path=Icon}" Height="16px" Width="16px"/>
</ResourceDictionary>
</ContextMenu.Resources>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{StaticResource menuIcon}"/>
<Setter Property="Command" Value="{Binding Command}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</ListBox.ContextMenu>
The icon with this solution is different for each item. The icon is defined in the property of the underlaying object.
I have a TabControl bound to an ObservableCollection property of view models, TabViewModelsCollection.
When another property gets set from within the view models, DeviceState, I'd like to raise a property changed event and tell my TabControls ItemSource to refresh.
The problem is my ItemSource is an ObservableCollection of ViewModels and when I call RaisePropertyChanged("TabViewModelsCollection"); nothing gets updated.
Furthermore my tab views contain multiple user controls and bindings.
The scenario that is supposed to play out is: A device is located on the network, data is collected, and the tabs of device information should then be updated.
Currently my TabControl only updates when I select a different device then select the device whos information I want to see. Think left panel list of devices, right panel with tabs of device info.
Let me know what part of the code you guys might want to see, my codebase is quite large so it would be hard to post it.
Here is where the TabControl is defined in my view:
<!-- Devist List Controls -->
<Grid DockPanel.Dock="Left" Margin="5,5">
<local:DeviceListView DataContext="{Binding DeviceListViewModel}" Grid.Row="0"/>
</Grid>
<GroupBox Header="Device Information" DockPanel.Dock="Right" Margin="0,0,5,5">
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection}" SelectedItem="{Binding DeviceListViewModel.SelectedDevice.SelectedTabItemVm}" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type vms:HomeViewModel}">
<local:HomeTab/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ConfigurationViewModel}">
<Grid>
<local:ConfigurationFileView Visibility="{Binding Configuration, TargetNullValue=Collapsed, FallbackValue=Visible}"/>
<local:ErrorTab Visibility="{Binding Path= Configuration, TargetNullValue=Visible, FallbackValue=Hidden}"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ExpansionModulesViewModelFactory}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<DockPanel >
<local:ExpansionModulesList Title="Discovered/Enumerated"
DataContext="{Binding DiscoveredModules}"
/>
<GridSplitter Width="5"/>
<local:ExpansionModulesList Title="User Action Required"
DataContext="{Binding FaultyModules}"
/>
</DockPanel>
</StackPanel>
<DockPanel Grid.Row="1">
<StackPanel Orientation="Horizontal" FlowDirection="RightToLeft" Margin="5" IsEnabled="{Binding IsCommandEnabled}">
<Button Content="Cancel" HorizontalAlignment="Right"
Command="{Binding CancelExpansionCommand }"
ToolTip="Revert all local modifications by refreshing data from the controller." />
<Separator Width="10"/>
<Button Content="Apply" HorizontalAlignment="Center"
Command="{Binding ApplyExpansionCommand }"
ToolTip="Apply all changes to the controller." />
<Separator/>
</StackPanel>
</DockPanel>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:LogViewModel}">
<local:LogView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:SignalStrengthViewModel}">
<local:SignalStrengthView />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.ItemContainerStyle>
EDIT: I want to be able to call raised property changed on this and have it refresh all of my tab views..
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection}" SelectedItem="{Binding DeviceListViewModel.SelectedDevice.SelectedTabItemVm}" >
RaisePropertyChanged("TabViewModelsCollection");
Hi please try the next solution:
VM code redaction
private State _deviceState;
private ObservableCollection<object> _tabViewModelsCollection;
public State DeviceState
{
get { return _deviceState; }
set
{
_deviceState = value;
RaisePropertyChanged("DeviceState");
UpdateTabViewModelsCollection();
}
}
public ObservableCollection<object> TabViewModelsCollection
{
get
{
return _tabViewModelsCollection ??
(_tabViewModelsCollection = new ObservableCollection<object>(GetDeviceData()));
}
}
private void UpdateTabViewModelsCollection()
{
_tabViewModelsCollection = null;
RaisePropertyChanged("TabViewModelsCollection");
}
private List<object> GetDeviceData()
{
//implement here the data collection process
throw new NotImplementedException();
}
Xaml redaction (define the UpdateSourceTrigger)
ItemsSource="{Binding DeviceListViewModel.SelectedDevice.TabViewModelsCollection, UpdateSourceTrigger=PropertyChanged}"
Let me know if it was helpful.
Regards.
I have a tabbed section and I am trying to wire up the commands for closing and opening new tabs dynamically. The problem is I cant understand how to bind the command from within my tabItem template(which has a button). Here is the code:
(UserControl containing the tabbed section, simplified..):
<UserControl.DataContext>
<vm:InicioViewModel />
</UserControl.DataContext>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Visual Resources/TabResource.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<ContentPresenter HorizontalAlignment="Stretch" Grid.Column="1">
<ContentPresenter.Content>
<TabControl Name="tc">
<TabControl.DataContext>
<vm:WorkSpaceViewModel/>
</TabControl.DataContext>
<TabControl ItemsSource="{Binding Items}"/>
</TabControl>
</ContentPresenter.Content>
</ContentPresenter>
</Grid>
(Here is the resource Dictionary for the tabItem):
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="TaBorder" Width="auto" Height="auto"
BorderBrush="LightGray"
BorderThickness="0.5,0.5,0.5,0"
CornerRadius="3,3,0,0"
Background="WhiteSmoke">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<TextBlock Margin="2" Grid.Column="0" Text="{TemplateBinding Header}" />
<Button x:Name="CloseButton" Grid.Column="1" Width="11" Height="11"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Background="Transparent"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=DataContext}"
Command="{Binding CloseWorkSpaceCommand}"
(Donkt know what yo put in command to reference my viewmodel Icommand)
BorderThickness="0"
>
<!-- ETC -->
(Here my viewModel):
class InicioViewModel : ViewModelBase
{
private WorkSpaceViewModel _workSpaceVm;
public InicioViewModel()
{
}
public WorkSpaceViewModel WorkSpaceVm
{
get { return _workSpaceVm; }
set { _workSpaceVm = value; }
}
}
(WorkSpaceViewModel..):
public class WorkSpaceViewModel
{
private ObservableCollection<IWorkSpaceItemVm> _items;
private RelayCommand _closeWorkSpaceCommand;
public WorkSpaceViewModel()
{
_items = new ObservableCollection<IWorkSpaceItemVm>();
}
public ObservableCollection<IWorkSpaceItemVm> Items
{
get { return _items; }
set { _items = value; }
}
public ICommand CloseWorkSpaceCommand
{
get
{
return _closeWorkSpaceCommand ?? (_closeWorkSpaceCommand = new RelayCommand(
param => CloseWorkSpace_Execute(param),
param => CloseWorkSpace_CanExecute(param)
));
}
}
private void CloseWorkSpace_Execute(object parm)
{
MessageBox.Show("asdasdasd");
}
private bool CloseWorkSpace_CanExecute(object parm)
{
return true;
}
}
As you can note I only have a MessageBox showing in CloseWorkSpace_Execute for test purposes.
1) How can I reference the Icommand in my viewmodel from within my tabItem style template, or, if is there a better way with same results will be welcome.
2) Why when I run the app one empty tab is created, I have my observable collection list empty
EDIT:
The RelayCommand is working OK in another part of the program, thats not the problem, The tabItem gets rendered OK with triggers workin and all, I still cant get my head into how to bind the command from my viewmodel with the Templated tabItem I made.
EDIT 2:
The command is working now, apparently the Command is not recognized in the resource dictionary and is marked as: "Cant resolve property 'CloseWorkSpaceCommand' in DataContext of type object", but setting the DataContext of the button to:
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=DataContext}"
gets the work done when running the app(visual studio still apologizing about DataContext type, dunno what it means).
There is still one tab created by default, why?
And is there a way to correct the code smell with DataContext type?
It seems like you want to add a close button on every tab like the ones we see in browsers. It's seems pretty complicated to do that. But let me try to break it down for you.
First let's start by stating the roadblocks that prevents us from doing that:
The TabItem does not have a command property where you can bind your CloseWorkSpaceCommand.
The tab item does not have a close button. That's the reason you created a Template. But still you can't do a template binding to a command property since the TabItem does not have such command property.
How will you be able wire up the button's command to the viewmodel's CloseWorkSpaceCommand property?
Now let's try to resolve each problem one by one.
To resolve this, we need to create a custom control for the TabItem that has a command property.
public class ClosableTabItem : TabItem
{
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(ClosableTabItem), new PropertyMetadata(null));
public ICommand CloseCommand
{
get { return (ICommand)GetValue(CloseCommandProperty); }
set { SetValue(CloseCommandProperty, value); }
}
}
Since we have a custom tab item, we also need a custom TabControl because we need to overide the GetContainerForItemOverride() method.
public class ClosableTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new ClosableTabItem();
}
}
This resolves problem #1.
Like what you did we need to have a ControlTemplate so we could place a close button on each tabs.
<ControlTemplate TargetType="{x:Type local:ClosableTabItem}">
<Border x:Name="TaBorder" Width="auto" Height="auto"
Background="LightGray" CornerRadius="4,4,0,0" Margin="0,2,3,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<TextBlock Margin="2" Grid.Column="0" Text="{TemplateBinding Header}" />
<Button x:Name="CloseButton" Grid.Column="1" Width="11" Height="11"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Command="{TemplateBinding CloseCommand}"
BorderThickness="0"
Content="X" Background="Red"
FontSize="8">
</Button>
</Grid>
</Border>
</ControlTemplate>
To bind the viewmodel.CloseWorkSpaceCommand to the tab item we do this in the ItemContainerStyle's setter.
<local:ClosableTabControl.ItemContainerStyle>
<Style TargetType="local:ClosableTabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="CloseCommand" Value="{Binding DataContext.CloseWorkSpaceCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> ...
You'll notice that I'm using relative source to the DataContext of the Window.
Now to bind the CloseCommand Property of the ClosableTabItem to the Command property of the Button inside the template, we now do the template binding inside the control template's button.
You can also see this in answer #2 code sample.
Command="{TemplateBinding CloseCommand}"
Here's the complete xaml code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<local:ClosableTabControl ItemsSource="{Binding Items}">
<local:ClosableTabControl.ItemContainerStyle>
<Style TargetType="local:ClosableTabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="CloseCommand" Value="{Binding DataContext.CloseWorkSpaceCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ClosableTabItem}">
<Border x:Name="TaBorder" Width="auto" Height="auto"
Background="LightGray" CornerRadius="4,4,0,0" Margin="0,2,3,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<TextBlock Margin="2" Grid.Column="0" Text="{TemplateBinding Header}" />
<Button x:Name="CloseButton" Grid.Column="1" Width="11" Height="11"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Command="{TemplateBinding CloseCommand}"
BorderThickness="0"
Content="X" Background="Red"
FontSize="8">
</Button>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:ClosableTabControl.ItemContainerStyle>
<TabControl.Items>
</TabControl.Items>
</local:ClosableTabControl>
</Grid>
</Window>
I have a Problem with the ComboBox at wpf and xaml. The ItemsSource of my combobox is a list of (CollarTypesImage).
the binding is work fine but the problem when editing data, i can't select the item of my ComboBox.. maybe its selected but the image is not view...
but when click ComboBox, i can see all itams:
http://www.ahmadabouhamdh.com/tmp_global/1.png
i used RelativeSource , nothing changed, ComboBox not selection the my saved item:
<UserControl x:Class="TailorManager.Views.OrderDetailItem"
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:vm="clr-namespace:TailorManager.ViewModels"
xmlns:Converter="clr-namespace:TailorManager.Converters"
mc:Ignorable="d"
d:DesignHeight="730" d:DesignWidth="556" FlowDirection="RightToLeft" >
<UserControl.Resources>
<!--<vm:OrderDetailItemViewModel x:Key="OrderDetailItemViewModel1" />-->
<Converter:ImageConverter x:Key="ImgConverter" />
</UserControl.Resources>
<Grid >
<ScrollViewer VerticalScrollBarVisibility="Auto" >
<ItemsControl>
<StackPanel>
<GroupBox Header="تفاصيل الياقة" Margin="5,5,5,0" VerticalAlignment="Top" Height="170">
<Grid>
<ComboBox ItemsSource="{Binding Path= DataContext.ImagesCollarTypes,
RelativeSource={RelativeSource AncestorType=UserControl,AncestorLevel=1}}"
SelectedValue="{Binding Path=OrderDetailItem.CollarTypesImage,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="{Binding Path=OrderDetailItem.CollarTypesImage}"
Margin="393,106,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="132" Height="38">
<ComboBox.ItemTemplate>
<DataTemplate>
<Image Width="50" Height="50" Source="{Binding Path= CollarImage,Converter={StaticResource ImgConverter} }" />
</DataTemplate>
</ComboBox.ItemTemplate >
</ComboBox>
</Grid>
</GroupBox>
<Grid/>
</StackPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>
i added
SelectedItem="{Binding Path=OrderDetailItem.CollarTypesImage}"
and nothing changed.
i use this Control inside window has a different DataContext type,i set the value of (DataContext OrderDetailItem) from constructor of this control.
image below explain the binding is working fine:
this image when click edit to show my saved image from DB, my item not selected:
http://www.ahmadabouhamdh.com/tmp_global/2.png
here all the code:
public List<CollarTypesImage> ImagesCollarTypes
{
get
{
//ImagesCollarTypes[0].CollarImage
if (_imagesCollarTypes.Count == 0)
{
TailorManagerDBEntities db = new TailorManagerDBEntities();
_imagesCollarTypes = db.CollarTypesImages.ToList();
}
return _imagesCollarTypes;
}
set
{
_imagesCollarTypes = value;
RaisePropertyChanged(() => ImagesCollarTypes);
}
}
public partial class CollarTypesImage
{
public CollarTypesImage()
{
this.OrderDetails = new HashSet<OrderDetail>();
}
public System.Guid CollarTypeId { get; set; }
public byte[] CollarImage { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
}
here the xaml code from window i used my control:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TailorManager.ViewModels"
xmlns:util="clr-namespace:TailorManagerLib.Business;assembly=TailorManagerLib"
xmlns:local="clr-namespace:TailorManager.Views"
x:Class="TailorManager.Views.AddOrder"
Title="AddCustomer" Height="665" Width="974" MinWidth="600" MinHeight="666" FlowDirection="RightToLeft" >
<Window.Resources>
<vm:ManageOrderDetilsViewModel x:Key="ManageOrderDetilsViewModel1" />
</Window.Resources>
<Grid DataContext="{StaticResource ManageOrderDetilsViewModel1}" x:Name="GridDataContaner">
<Grid.Background>
...
</Grid.Background>
<GroupBox Header="تفاصيل الطلب" Margin="10,160,10,0" FlowDirection="RightToLeft">
<Grid Grid.Column="0" Name="GridOrderDetails">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="ColumnDefinitionListDetails" Width="183*"/>
<ColumnDefinition Name="ColumnDefinitionDetails" Width="0*"/>
</Grid.ColumnDefinitions>
<Grid Margin="0,0,0,40" >
...
</Grid>
<Grid Grid.Column="1">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="3" Margin="4" >
<Grid>
<local:OrderDetailItem x:Name="OrderDetailItemControl" VerticalAlignment="Top" />
</Grid>
</Border>
</Grid>
...
</Grid>
</GroupBox>
</Grid>
</Window>
how i set the DataContext of the Control, this inside the command in VM of my window:
OrderDetailItemViewModel OrdDetailItem = new OrderDetailItemViewModel(Ord.OrderDetailsId);
OrderDetailItemControl.DataContext = OrdDetailItem;
Please how to fix it??
UPDATE
when i change the relative source as below:
<ComboBox Name="CmbBxImgCollarTyp" ItemsSource="{Binding Path= DataContext.ImagesCollarTypes, RelativeSource={
RelativeSource AncestorType={x:Type vm:OrderDetailItemViewModel}}"
SelectedValue="{Binding Path=OrderDetailItem.CollarTypesImage, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="{Binding Path= OrderDetailItem.CollarTypesImage}" Margin="393,106,0,0" VerticalAlignment="Top"
HorizontalAlignment="Left" Width="132" Height="38">
<ComboBox.ItemTemplate>
<DataTemplate>
<Image Width="50" Height="50" Source="{Binding Path=CollarImage,
Converter={StaticResource ImgConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
the binding never work at all modes!, i change it to this way cause the datacontext of this control is an object of the viewmodel "OrderDetailItemViewModel"
What should be fix؟؟
You have an error in your RelativeSource Binding Path. You have used the type of UserControl, but the UserControl class does not have a property named ImagesCollarTypes... instead, you should use your UserControl where you defined the property... I'm assuming that class was named OrderDetailItem:
<ComboBox ItemsSource="{Binding Path= DataContext.ImagesCollarTypes, RelativeSource={
RelativeSource AncestorType={x:Type YourViewsPrefix:OrderDetailItem}}}"
SelectedValue="{Binding Path=CollarTypesImage, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="{Binding Path=
CollarTypesImage}" Margin="393,106,0,0" VerticalAlignment="Top"
HorizontalAlignment="Left" Width="132" Height="38">
<ComboBox.ItemTemplate>
<DataTemplate>
<Image Width="50" Height="50" Source="{Binding Path=CollarImage,
Converter={StaticResource ImgConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You should have received an error in the Output Window in Visual Studio saying something like Error: property 'ImagesCollarTypes' not found on object 'UserControl' or something similar.
Disclaimer: I didn't go through all of your code because you simply have too much there, so even after you fix this error, your code may still have other errors and not work as expected.
UPDATE >>>
For this Binding to work, the following assumptions were made:
The UserControl.DataContext must have a valid instance of an object set.
The object set as the UserControl.DataContext must have a public property named ImagesCollarTypes.
The ImagesCollarTypes property must be of a type that contains a public property named CollarTypesImage.
after check the Output window, i notes this error:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor, AncestorType='Projectname.ViewModels.ViewModel', AncestorLevel='1''. BindingExpression:Path=LookupItems; DataItem=null; target element is 'ComboBox' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
then i change the RelativeSource from AncestorType from my ViewModel to UserControl, just like this:
RelativeSource={ AncestorType={x:Type UserControl},AncestorLevel=1}}"
and now everything working fine,
Thanks for members make an effort
I have the following code that creates a TabControl. Each tab contains a UserControl (code is below) that displays different data (one shows Local tax info and the other show Fed/State tax info).
TabControl
<TabControl
Name="MappingTabs"
Margin="6,7,7,8" Padding="6"
Background="White" >
<TabItem
Name="LocalTaxTab"
Padding="6,1"
Header="Local">
<AdornerDecorator>
<DockPanel>
<Border Margin="7">
<GroupBox
Name="LocalTaxesGroup">
<GroupBox.Header>
<TextBlock
FontWeight="Bold"
Text="Local Taxes">
</TextBlock>
</GroupBox.Header>
<StackPanel Margin="20,8,10,0"
Orientation="Vertical">
<local:TaxCodeMappingHeader />
<!-- Note that a row is 25 high, -->
<ScrollViewer
MaxHeight="250"
>
<ItemsControl
Name="LocalTaxCodeMappingControl"
ItemTemplate="{StaticResource MappingRuleTemplate}"
BorderThickness="0"
AlternationCount="2"
IsTextSearchEnabled="False"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding TaxCodesCollection[0].CodeCollection, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<!-- ItemsSource="{Binding Source={StaticResource sortedCodeCollection}}"> -->
</ItemsControl>
</ScrollViewer>
<local:TaxCodeMappingFooter DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</GroupBox>
</Border>
</DockPanel>
</AdornerDecorator>
</TabItem>
<TabItem
Name="FedStateTaxesTab"
Padding="6,1"
Header="Federal\State">
<AdornerDecorator>
<DockPanel>
<Border Margin="7">
<GroupBox
Name="FedStateTaxesGroup">
<GroupBox.Header>
<TextBlock
FontWeight="Bold"
Text="Federal \ State Taxes">
</TextBlock>
</GroupBox.Header>
<StackPanel Margin="20,8,10,0"
Orientation="Vertical">
<local:TaxCodeMappingHeader />
<!-- Note that a row is 25 high, -->
<ScrollViewer
MaxHeight="250"
>
<ItemsControl
Name="FedStateTaxCodeMappingControl"
ItemTemplate="{StaticResource MappingRuleTemplate}"
BorderThickness="0"
AlternationCount="2"
IsTextSearchEnabled="False"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding TaxCodesCollection[1].CodeCollection, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<!-- ItemsSource="{Binding Source={StaticResource sortedCodeCollection}}"> -->
</ItemsControl>
</ScrollViewer>
<local:TaxCodeMappingFooter DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</GroupBox>
</Border>
</DockPanel>
</AdornerDecorator>
</TabItem>
</TabControl>
</StackPanel>
UserControl (TaxCodeMappingFooter)
<Button
Name="AddButton"
Grid.Row="0"
Grid.Column="0"
Height="20" Width="20"
Command="{Binding Path=DataContext.AddClickCommand}"
CommandParameter="(want the tab name here)"
Style="{StaticResource ImageButton}"
ToolTip="Add a rule"
local:AttachedImage.Image="{StaticResource AddImageSource}" />
The UserControl (TaxCodeMappingFooter) contains an Add button that I need to wire up via RelayCommand to the VM. I need to somehow tell the VM which tab is calling the Add command so that an item can be added to the correct collection. I thought about sending the TabName and then keying off that to know which tab the user is on.
Is my idea correct or is the a better way to do this and if it is correct how do I get the TabName value to pass it back as a CommandParameter?
If you are going to hard code your UI controls as you have done, then perhaps your simplest option is to define a string DependencyProperty in your TaxCodeMappingFooter control:
public static readonly DependencyProperty TabNameProperty = DependencyProperty.
Register("TabName", typeof(string), typeof(TaxCodeMappingFooter));
public string TabName
{
get { return (string)GetTabName(TabNameProperty); }
set { SetTabName(TabNameProperty, value); }
}
Then you could set it from your TabItems:
<local:TaxCodeMappingFooter TabName="FedStateTaxesTab" DataContext="{Binding
RelativeSource={RelativeSource AncestorType=UserControl}}" />
And Bind to it from inside your control:
<Button Name="AddButton" Command="{Binding Path=DataContext.AddClickCommand}"
CommandParameter="{Binding TabName, RelativeSource={RelativeSource
AncestorType=TaxCodeMappingFooter}}" ... />
As others have said, if you model your view model structure appropriately, this would not be much of an issue.
If you really want to bind against an ancestor element, you can use a RelativeSource of FindAncestor, then specify the AncestorType. Note that you may need to tweak AncestorLevel if you are the descendant of more than one TabItem.
{Binding Path=Name
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type TabItem}}}
(wrapping added for clarity)