binding combobox images - how to select saved item in combobox images - c#

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

Related

ObservableCollection not Sorting

I am writing an MVVM application but I am having trouble with an Observable Collection.
The ObservableCollection is held in the view model as:
private ObservableCollection<Participant> _initiativeList;
public ObservableCollection<Participant> InitiativeList
{
get { return _initiativeList; }
set
{
_initiativeList = value;
OnPropertyChanged("InitiativeList");
}
}
In the XAML I have a list box :
<ListBox x:Name="lvInitiativeList"
DockPanel.Dock="Top"
ItemsSource="{Binding Source={StaticResource InitiativeListCollection}}"
ItemTemplate="{StaticResource ResourceKey=ParticipantDisplayPanel}"
SelectedItem="{Binding Path=SelectedParticipant}"/>
The collectionViewSource it uses is:
<CollectionViewSource Source="{Binding Path=InitiativeList, Mode=OneWay}"
x:Key="InitiativeListCollection">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="InitiativeScore" Direction="Descending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
and the Item Template is:
<DataTemplate x:Key="ParticipantDisplayPanel">
<uc:ParticipantDisplayPanel/>
</DataTemplate>
the ParticipantDisplayPanel is a UserControl defined as:
<UserControl x:Class="InitiativeList.View.UserControls.ParticipantDisplayPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:InitiativeList.View.UserControls">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Resources\CommonStyles.xaml"/>
<ResourceDictionary Source="..\Resources\ConverterDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Border BorderBrush="{Binding Path=ActionState, Mode=OneWay, Converter={StaticResource BorderHighlight}}"
BorderThickness="3"
CornerRadius="10"
Background="{Binding Path=Kind, Mode=OneTime, Converter={StaticResource KindColor}}">
<StackPanel Orientation="Horizontal"
Width="450"
Height="50"
Background="Transparent">
<Border BorderBrush="Black"
BorderThickness="2"
Width="50"
Height="50"
CornerRadius="25"
Background="#FF8040">
<TextBlock x:Name="tbInitiative"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="30"
Text="{Binding Path=InitiativeScore,
Mode=OneWay,
Converter={StaticResource InitiativeScore}}"/>
</Border>
<TextBlock x:Name="tbName"
Background="Transparent"
Width="250"
Height="25"
FontFamily="Ariel"
FontWeight="Bold"
FontSize="20"
Text="{Binding Path=Name, Mode=OneWay}"/>
<TextBlock x:Name="Condition"
Background="Transparent"
Width="100"
Height="25"
Text="{Binding Path=Condition, Mode=OneWay}"/>
<Border BorderBrush="Black"
BorderThickness="2"
Width="50"
Height="50"
CornerRadius="25"
Background="{Binding Path=Healthiness, Mode=OneWay,
Converter={StaticResource HPBackground}}">
<TextBlock x:Name="tbHitPoints"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="25"
Text="{Binding Path=CurrentHitPoints, Mode=OneWay}"/>
</Border>
</StackPanel>
</Border>
</UserControl>
When I add items to the observable collection the items appear in the ListBox, and when I update properties of the elements the ParticipantDisplayPanels update to reflect the changed data. However the collection does not sort in response to these changes, unless I specifically call OnPropertyChanged for the list, something I was expecting to be done as part of the function of the ObservableCollection, or at least through the Set code on the InitiativeList Property.
It feels wrong to have to pepper the OnPropertyChanged calls throughout the code. Am I doing something wrong? all my searches on the internet haven't turned up an example of an Observable Collection that I can relate to how I've written my code.
Any pointers as to what is going wrong would be much appreciated.
I want to implement some custom sorting on Observable collection and I did it like below and then bind:
ItemsSource="{Binding CollectionView}"...
List<SortDescription> SortDescriptions = new List<SortDescription>();
SortDescriptions.Add(new SortDescription("Field1", ListSortDirection.Ascending));
SortDescriptions.Add(new SortDescription("Field2", ListSortDirection.Ascending));
SortDescriptions.Add(new SortDescription("Field3", ListSortDirection.Ascending));
SortDescriptions.Add(new SortDescription("Field4", ListSortDirection.Ascending));
CollectionViewSource collectionViewSource = new CollectionViewSource();
public ICollectionView CollectionView
{
get
{
collectionViewSource.Source = <YourObservableCollection>;
if (SortDescriptions != null)
{
foreach (SortDescription sd in SortDescriptions)
{
collectionViewSource.View.SortDescriptions.Add(sd);
}
}
collectionViewSource.View.Refresh();
return collectionViewSource.View;
}
}

Updating TabControl ItemSource ViewModels when specific state changes

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.

Updating Listview after adding/removing item

How can I refresh ListView after adding item to collection in Windows Store app? Adding items to list works fine, but Listview doesn't refresh. I was trying to implement INotifyCollectionChanged, but what exactly should I do to make it work?
edit: XAML file
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView HorizontalAlignment="Left" Height="437" Margin="10,120,0,0" VerticalAlignment="Top" Width="593" ItemsSource="{Binding Persons, Mode=TwoWay}" Background="#FF5D5D5D">
<ListView.DataContext>
<Model:School/>
</ListView.DataContext>
</ListView>
<Button Content="Button" HorizontalAlignment="Left" Margin="7,64,0,0" VerticalAlignment="Top" Command="{Binding AddCommand, Mode=OneWay}">
<Button.DataContext>
<Model:School/>
</Button.DataContext>
</Button>
</Grid>
C# code:
class School
{
private ObservableCollection<string> _persons = new ObservableCollection<string>()
{
"Name1", "Name2", "Name3"
};
public ObservableCollection<string> Persons
{
get { return _persons; }
}
private ICommand _addCommand;
public ICommand AddCommand
{
get
{
return this._addCommand ??
(this._addCommand = new RelayCommand(Add));
}
}
private void Add()
{
this._persons.Add("Name");
}
}
You don't need to add INotifyCollectionChanged when you are using ObservableCollection - it already implements needed interfaces.
Your code should work, though there may be some other problems:
check if the DataContext of your ListView (its parent in this case) is properly set - it should be set, so that 'ListView would find' Persons property,
also check if the ItemTemplate is properly set - example:
<ListView Name="myList" ItemsSource="{Binding Persons}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Surname}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in your case there is no need that your ListView's ItemsSource uses TwoWay binding - you are not defining a setter ofr the property Persons.
EDIT:
After your edit, I can see where is the problem - you are setting separate DataContext for your ListView and Button - the button is adding to its own collection - different than ListView is bound to. Take a short test - set the DataContext of both to same staticresource:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Model:School x:Key="mySchool"/>
</Grid.Resources>
<ListView HorizontalAlignment="Left" VerticalAlignment="Stretch" Margin="10,120,0,0" ItemsSource="{Binding Persons}" Background="#FF5D5D5D"
DataContext="{StaticResource mySchool}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="7,64,0,0" VerticalAlignment="Top" Command="{Binding AddCommand, Mode=OneWay}"
DataContext="{StaticResource mySchool}">
</Button>
</Grid>

Binding BooleanToVisibilityConverter to a Control in a TreeView HierarchicalDataTemplate

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.

Get TabItem Name in UserControl

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)

Categories

Resources