WPF ComboBox selection change after switching tabs - c#

I made a project based on nested tabs.
the nested tabs are different instance of the same viemModel and the same UI.
when I switch between the tabs he comboboxes present in the tabs chenge thei selection depending on the tab that is loosing focus.
I add both the viewmodels and the view of my test project.
thank you in advance for your help
main window
<Window.Resources>
<DataTemplate DataType="{x:Type local:IntermediateViewModel}">
<local:IntermediateView />
</DataTemplate>
<DataTemplate x:Key="HeaderedTabItemTemplate">
<Grid>
<ContentPresenter
Content="{Binding Path=Header, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center" >
</ContentPresenter>
</Grid>
</DataTemplate>
<Style x:Key="SimpleTabItemStyle" TargetType="TabItem">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border" BorderThickness="1" BorderBrush="#555959">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center"
ContentSource="Header" Margin="12,2,12,2" RecognizesAccessKey="True" Height ="40" MinWidth ="90"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="#555959" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="DefaultTabControlTemplate">
<TabControl IsSynchronizedWithCurrentItem="True"
BorderThickness="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource HeaderedTabItemTemplate}"
ItemContainerStyle="{StaticResource SimpleTabItemStyle}"
SelectionChanged="TabControl_SelectionChanged"
/>
</DataTemplate>
<!---->
</Window.Resources>
<Grid MinHeight="200" MinWidth="300">
<Grid.RowDefinitions>
<RowDefinition Height="260*" />
<RowDefinition Height="51*" />
</Grid.RowDefinitions>
<Border >
<ContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{DynamicResource DefaultTabControlTemplate}"
/>
</Border>
<Button Grid.Row="1" Content="Add" Command="{Binding AddCommand}"/>
</Grid>
view model (create a different istance each time)
class MainWindowViewModel : WorkspacesViewModel<IntermediateViewModel>
{
public MainWindowViewModel()
{
this.WorkspacesView.CurrentChanged += new EventHandler(WorkspacesView_CurrentChanged);
}
void WorkspacesView_CurrentChanged(object sender, EventArgs e)
{
}
RelayCommand myVar = null;
public ICommand AddCommand
{
get
{
return myVar ?? (myVar = new RelayCommand(param =>
{
SetWindow(new IntermediateViewModel("AA" + this.Workspaces.Count) );
}));
}
}
first level tab
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:ClassViewModel}">
<local:ClassView />
</DataTemplate>
</UserControl.Resources>
<Border>
<ContentControl Content="{Binding Path=CurrentWorkspace, Mode=OneWay}" Loaded="ContentControl_Loaded" DataContextChanged="ContentControl_DataContextChanged" IsVisibleChanged="ContentControl_IsVisibleChanged" LayoutUpdated="ContentControl_LayoutUpdated" TargetUpdated="ContentControl_TargetUpdated" Unloaded="ContentControl_Unloaded" />
</Border>
first level viewmodel
class IntermediateViewModel : WorkspacesViewModel
{
public string Header { get; set; }
public IntermediateViewModel(string header)
{
Header = header;
SetWindow(new ClassViewModel(header));
}
}
nested tab
<UserControl.Resources>
<CollectionViewSource x:Key="StatusView" Source="{Binding Path=StatusList}"/>
</UserControl.Resources>
<Grid>
<ComboBox Name="_spl2Status" ItemsSource="{Binding Source={StaticResource StatusView}}"
SelectedValue="{Binding Path=MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="FL_TYPE"
DisplayMemberPath="ID_TYPE" Margin="76,12,0,0" Height="40" VerticalAlignment="Top" HorizontalAlignment="Left" Width="146"
DataContextChanged="_spl2Status_DataContextChanged"
IsVisibleChanged="_spl2Status_IsVisibleChanged"
Loaded="_spl2Status_Loaded"
SelectionChanged="_spl2Status_SelectionChanged"
>
</ComboBox>
</Grid>
nested tab view model
public enum myTypes
{
tipo0 = 0,
tipo1 = 1,
tipo2 = 2,
}
class ClassViewModel : WorkspaceViewModel
{
public ClassViewModel(string name)
{
Name = name;
}
public string Name { get; set; }
private List<IntEnumType> _statusList = null;
public List<IntEnumType> StatusList
{
get
{
if (_statusList == null)
_statusList = new List<IntEnumType>()
{
new IntEnumType((int)myTypes.tipo0, myTypes.tipo0.ToString()),
new IntEnumType((int)myTypes.tipo1, myTypes.tipo1.ToString()),
new IntEnumType((int)myTypes.tipo2, myTypes.tipo2.ToString()),
};
return _statusList;
}
}
private int myVar = 1;
public int MyProperty
{
get
{
return myVar;
}
set
{
if (myVar != value)
{
myVar = value;
OnPropertyChanged(() => MyProperty);
}
}
}
}
public class TabItemStyleSelector : StyleSelector
{
public Style MainTabItem { get; set; }
public Style ChildrenTabItem { get; set; }
public Style SpecificationTabItem { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
//if (item is IHome)
// return MainTabItem;
//else if (item is SpecificationItemViewModel)
// return SpecificationTabItem;
//else
return ChildrenTabItem;
}
}

The code is a little hard to completely follow, but I'm guessing that the issue is that there is only one instance of your ClassViewModel and it is where the selection for the combo box is stored {Binding Path=MyProperty, so whatever is stored in MyProperty will be reflected in all instances of the combo box regardless of where they live.

Well this is a bit late, but as I'm facing the same issue, I want to share my analysis.
When you change your tabs, you change the DataContext of the current Tab to your other ViewModel and hence also the ItemsSource of your ComboBox.
In case your previously selected Item (SelectedItem) is not contained within the new ItemsSource, the ComboBox fires a SelectionChanged-Event and therefore sets the SelectedIndex to -1.
Altough this default behaviour of the ComboBox might make sense, it's very annoying in many cases.
We've derived an own class from ComboBox, handling that. But it's not very satisfying as you loose some default behaviour you most probably need.

The problem is in your loaded event handlers.
When you switch tabs your unloading one tab and loading a new one.
I suspect your changing MyComboBox.SelectedIndex in _spl2Status_Loaded.

Related

How to add grid to my combobox and display obervableCollection data?

I use a combobox and I would like to proceed as follows:
I choose an element in cmb1, this allows to display in cmb2 a Collection.
This code allows me to retrieve the data I want ( result A = ObservableCollectionA, result B = ObservableCollection B...)
<ComboBox Name="cmbResultatListe"
Margin="0,10,0,10"
Grid.Row="4"
Grid.Column="2"
Height="25"
Width="250" >
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Sections}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedChoiceList}" Value="Etablissement">
<Setter Property="ItemsSource" Value="{Binding Etablissements}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedChoiceList}" Value="Service">
<Setter Property="ItemsSource" Value="{Binding Services}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
I would now like to divide my combobox into three grids, so that I can proceed as follows:
If result A is selected => cmb2 grid0 = ObservableCollectionA.ID, cmb2 grid1 = observableCollectionA.Name...
If result B is selected => cmb2 grid0 = ObservableCollectionB.Name, cmb2 grid1 = observableCollectionB.Years...
And i don't know how can i do that.
Any tips ?
Thank you for your help.
Edit :
c# code :
private ObservableCollection<Etablissement> _EtablissementsUtilisateur;
public ObservableCollection<Etablissement> EtablissementsUtilisateur
{
get
{
return _EtablissementsUtilisateur;
}
set
{
if (value != _EtablissementsUtilisateur)
{
_EtablissementsUtilisateur = value;
RaisePropertyChanged(nameof(EtablissementsUtilisateur));
}
}
}
private ObservableCollection<ServiceSectionModel> _Services;
public ObservableCollection<ServiceSectionModel> Services
{
get
{
return _Services;
}
set
{
if (value != _Services)
{
_Services = value;
RaisePropertyChanged(nameof(Services));
}
}
}
private ObservableCollection<ServiceSectionModel> _Sections;
public ObservableCollection<ServiceSectionModel> Sections
{
get
{
return _Sections;
}
set
{
if (value != _Sections)
{
_Sections = value;
RaisePropertyChanged(nameof(Sections));
}
}
}
private string _SelectedChoiceList;
public string SelectedChoiceList
{
get
{
return _SelectedChoiceList;
}
set
{
if (value != _SelectedChoiceList)
{
_SelectedChoiceList = value;
RaisePropertyChanged(nameof(SelectedChoiceList));
}
}
}
Etablissements = new ObservableCollection<Etablissement>((await _dataService.GetEtablissements().ConfigureAwait(false)));
Services = await _dataService.GetServicesAsync(false).ConfigureAwait(false);
Sections = await _dataService.GetSectionsAsync(_dataService.ParamGlobaux.IDEtablissement).ConfigureAwait(false);
Etablissement contain ID, Name, Years.
Service contain Color, ID, Name.
Section contain Color, ID, SectionName.
Edit 2 : I would like something like this exemple :
<ComboBox Name="CbService" HorizontalAlignment="Left" Margin="115,67,0,0" VerticalAlignment="Top" Width="150" ItemsSource="{Binding}" SelectionChanged="CbRecherche_SelectionChanged" KeyboardNavigation.TabIndex="1" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Libelle}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="gd" TextElement.Foreground="Black" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Fill="{Binding Fond}" Grid.Column="0"/>
<TextBlock Margin="5" Grid.Column="0" Text="{Binding ID}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding Libelle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
Currently my combobox displays a string. I want something like this :
In this example, there is an ID, a color only in the ID part, and a Name. I can't do that with my string at the moment.
I belive you may be able to reduce the size of your codes by removing the RaisePropertyChanged event as ObservableCollections already contain the INotifyPropertyChanged interface, I have made a simple example of how to use Datatemplate to display information from ObservableCollections.
Step 1: C# codes:
namespace WpfApp1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ComboBox1.ItemsSource = Services;
Services.Add(new ServiceSectionModel { Color = Brushes.Red, ID = "Clem", Name = "Clementine" });
Services.Add(new ServiceSectionModel { Color = Brushes.White, ID = "011", Name = "Logistique" });
Services.Add(new ServiceSectionModel { Color = Brushes.Green, ID = "MBT", Name = "Montbrilland" });
}
public class ServiceSectionModel
{
public string ID { get; set; }
public string Name { get; set; }
public SolidColorBrush Color { get; set; }
}
ObservableCollection<ServiceSectionModel> Services = new ObservableCollection<ServiceSectionModel>();
}
}
Step 2: XAML codes:
<ComboBox x:Name="ComboBox1" HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Background="{Binding Color}" Text="{Binding ID}" Margin="0,0,10,0" Padding="5,2,10,2"></TextBlock>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Header element within a ListView in UWP

I'd like to have a header element in my ListView, but I need the possibility to decide which item is simple and which one is header when I bind data to the ListView. This header should not be clickable and have a different color at least. In Android we do it in a custom adapter.
At the moment I get this logic of data binding to the ListView:
<ListView
x:Name="drawerListOptions"
SelectionChanged="onSelectDrawerItem"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<!--for simplicity I put only one view in as a list item -->
<TextBlock
Text="{Binding titleItemMenu}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and C# code:
drawerListOptions.ItemsSource = myListOfData;
So I need to check in my myListOfData if item is simple or header. Is there a way to do it?
by using CollectionViewSource
although i have the same problem, i searched over web but couldn't find solution so i create this one to load data from server and show in list as group separated as StickyHeaderListView in android.
in XAML
<Page
....
>
<RelativePanel ... >
<Page.Resources>
<CollectionViewSource x:Key="cvs" x:Name="cvs" SourceGrouped="True"/>
</Page.Resources>
<ListView x:Name="listView" IsItemClickEnabled="True" ItemsSource="{Binding Source={StaticResource `cvs` }}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate >
<RelativePanel HorizontalAlignment="Stretch" VerticalAlignment="Center" BorderBrush="DarkGray" BorderThickness="0,0,0,1" Background="White" Padding="10">
<TextBlock Text="{Binding Description}" RelativePanel.AlignVerticalCenterWithPanel="True" VerticalAlignment="Center" Foreground="Gray" FontSize="20"></TextBlock>
<StackPanel Orientation="Horizontal" RelativePanel.AlignRightWithPanel="True" RelativePanel.AlignVerticalCenterWithPanel="True" Margin="0,0,8,0" >
<TextBlock Text="{Binding Amount}" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{StaticResource primary}" FontSize="16"/>
</StackPanel>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate >
<DataTemplate>
<TextBlock Text="{Binding Key}" FontSize="14" Foreground="#FF222222" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="ListViewHeaderItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</RelativePanel> </Page>
and in source file
bind source
var obj = JsonConvert.DeserializeObject<List<Model.YourModelHere>>(ApiResponseHere);
var groups = from Data in obj group Data by Data.Date;
this.cvs.Source = groups;
Have an property in you collection item called IsSimple. In your xaml bind that variable to Header property of list view with converter. That Converter converts bool to Visibility. If value us true converter should return Visibility.Collapsed.
In UWP, we can use CollectionViewSource to provides a data source that adds grouping and current-item support to collection classes.
Use CollectionViewSource when you want to bind list controls to collections, but you want to display those collections in groups and maintain a current item independent from the list control.
For more info, see CollectionViewSource.
In my words, when you want bind to the collections and display those collections in groups without manipulating, we can use CollectionViewSource in this scenario.
You can use LINQ query to get results from the ObservableCollection. And you can set the results to the CollectionViewSource.Source.
In ListView, we can set the ListView.ItemSource bind to CollectionViewSource.View. It can get the view object that is currently associated with this instance of CollectionViewSource.
For example:
Create GroupInfoList class:
public class GroupInfoList : List<object>
{
public object Key { get; set; }
}
Create Text class:
public class Text : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
public Text(string name)
{
this.Title = name;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Add Header class:
public class Header
{
public string HeaderTitle { get; set; }
public Header()
{
HeaderTitle = string.Empty;
}
public static ObservableCollection<Text> GetTexts()
{
ObservableCollection<Text> myListOfData = new ObservableCollection<Text>();
myListOfData.Add(new Text("Product1"));
myListOfData.Add(new Text("Product2"));
myListOfData.Add(new Text("Product3"));
myListOfData.Add(new Text("Setting1"));
myListOfData.Add(new Text("Setting2"));
myListOfData.Add(new Text("Setting3"));
myListOfData.Add(new Text("Setting4"));
return myListOfData;
}
public static ObservableCollection<GroupInfoList> GetItemsGrouped()
{
ObservableCollection<GroupInfoList> groups = new ObservableCollection<GroupInfoList>();
var query = from item in GetTexts()
group item by item.Title[0] into g
orderby g.Key
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
GroupInfoList info = new GroupInfoList();
if (g.GroupName.ToString() == "P")
{
info.Key = "Products";
}
else if (g.GroupName.ToString() == "S")
{
info.Key = "Settings";
}
else
{
info.Key = g.GroupName;
}
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
}
In XAML:
<Page.Resources>
<CollectionViewSource x:Name="MyItems" IsSourceGrouped="True" />
<DataTemplate x:Name="ItemListViewTemplate">
<TextBlock Text="{Binding Title}"></TextBlock>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind MyItems.View}"
SelectionMode="Single"
ShowsScrollingPlaceholders="True"
Grid.Row="1"
ItemTemplate="{StaticResource ItemListViewTemplate}"
Grid.ColumnSpan="2">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding Key}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
Set the CollectionViewSource source in code behind:
public MainPage()
{
this.InitializeComponent();
MyItems.Source = Header.GetItemsGrouped();
}

Adding buttons to WPF from ObservableCollection using MVVM pattern

I'm trying to add buttons to UserControl. I have to follow MVVM pattern. I have created a class, DeviceButton, with different set/gets and a constructor. In a viewmodel, used as datacontext, is the ObservableCollection and a get-method for the collection. I have bound the collection to a ItemsControl source and tried to add a template. I guess I'm missing something 'cause the buttons won't load.
The UserControl is added into a tab (using dragablz) which as well is a part of a ObservableCollection, also added at run time (this is working just fine). The idea is, that the tab has a list of buttons that has to be created at run time, where the list is fetched from a web service - so the buttons has to be added dynamically/programatically. Overview is just the first tab - a template for each tab (reflecting the fetched items) is being implemented when the buttons work. For now, I'm just adding a test button to the collection, but as stated, this won't show. What am I missing?
I have a Overview.xaml file:
<UserControl // attributes omitted to save spaces... ask if needed>
<StackPanel>
<Border>
<TextBlock FontSize="16" FontWeight="Bold" TextWrapping="WrapWithOverflow"
TextAlignment="Center" HorizontalAlignment="Center"
Foreground="{DynamicResource AccentColorBrush}">
Welcome to Greenhouse App
</TextBlock>
</Border>
<ItemsControl ItemsSource="{Binding DeviceButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vmodel:DeviceButton}">
<Button Width="50" Height="50" Margin="10 10 0 0" Command="{Binding OpenTab}"
CommandParameter="{Binding DeviceType}" HorizontalAlignment="Left"
VerticalAlignment="Top" Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="{Binding ImageUrl}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// Manually added test button...
<Button x:Name="windMill" HorizontalAlignment="Left" Margin="10,0,0,0"
VerticalAlignment="Top" Width="50" Height="50"
Command="{Binding OpenTab}" FontFamily="Segoe Ui"
Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="/Greenhouse;component/Icons/Windmill.png" />
</StackPanel>
</Button
</StackPanel>
I'm trying to add an ObservableCollection of type DeviceButton, the _deviceButtons collection (Binded as ItemsSource to the ItemsControl)
The tabList items are working just find, and I can manually add more if needed (these will later be added through the OpenNewTab-command, which should be bound to the buttons)
The DeviceButton file:
public class DeviceButton
{
private readonly string _content;
private readonly string _deviceType;
private readonly string _imageUrl;
public DeviceButton(string content, string deviceType, string imageUrl)
{
_content = content;
_deviceType = deviceType;
_imageUrl = imageUrl;
}
public string Content
{
get { return _content; }
}
public string DeviceType
{
get { return _deviceType; }
}
public string ImageUrl
{
get { return _imageUrl; }
}
}
The collection is located in a viewmodel file, MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
public ICommand OpenTab { get; set; }
private string tabControl { get; set; } = "0";
private IInterTabClient _interTabClient;
private ObservableCollection<TabContent> _tabContents = new ObservableCollection<TabContent>();
private ObservableCollection<DeviceButton> _deviceButtons = new ObservableCollection<DeviceButton>();
public MainWindowViewModel()
{
_interTabClient = new MyInterTabClient();
DeviceButtons.Add(new DeviceButton("Windturbine", "windturbine", "/Greenhouse;component/Icons/Windmill.png"));
TabContents.Add(new TabContent("Overview", new Overview()));
OpenTab = new RelayCommand<object>(OpenNewTab);
}
private void OpenNewTab(object obj)
{
MessageBox.Show("Yo"); // Not yet implemented
RaisePropertyChanged(() => tabControl);
}
public ObservableCollection<TabContent> TabContents
{
get { return _tabContents; }
}
public ObservableCollection<DeviceButton> DeviceButtons
{
get { return _deviceButtons; }
}
public IInterTabClient InterTabClient
{
get { return _interTabClient; }
}
}
The buttons aren't loaded when I start the program (only the "test" button added manually in the WPF).
The UserControl, Overview, is considered a tab in another Controls.Metrowindow, MainWindow.xaml:
<Window.Resources>
<Style TargetType="{x:Type dragablz:TabablzControl}">
<Setter Property="CustomHeaderItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<ContentPresenter Margin="4" Content="{Binding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<dragablz:TabablzControl SelectedIndex="{Binding tabControl}" ItemsSource="{Binding TabContents}" x:Name="InitialTabablzControl" Margin="4 0 4 4">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController InterTabClient="{Binding MyInterTabClient}" />
</dragablz:TabablzControl.InterTabController>
</dragablz:TabablzControl>
I guess it's an issue with the resources/binding in the Overview.xaml, but I've exhausted all suggested solutions I could find.
Issue was in binding
ItemsSource="{Binding Path=DataContext.TabContents,
RelativeSource={RelativeSource AncestorLevel=1,
AncestorType={x:Type Window}, Mode=FindAncestor}}"
I suspect the DataContext is being set to a TabContent object, which does not have a DeviceButtons property
I would suggest using a tool like Snoop to verify your DataContext is what you expect.

unable to get, set selectedItem from the listbox containing radiobuttons being generated dynamically in wpf MVVM

Am trying to populate listbox with dynamic radiobuttons which are being customized to togglebuttons. I could populate listbox items with radiobuttons as said above. However, once we select any of the radiobuttons am unable to set the selected item from list of radiobuttons in my viewmodel object while debugging.
The following is the xaml code in my resource directory
<Style x:Key="ScreensList" TargetType="{x:Type ListBox}">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Margin" Value="2, 2, 2, 0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent">
<RadioButton
VerticalAlignment="Center" GroupName="{Binding RelativeSource={RelativeSource TemplatedParent}}"
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}">
<RadioButton.Template>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
<StackPanel Width="80" Height="60" Orientation="Vertical" HorizontalAlignment="Left" Margin="10,10,20,10">
<Image Source="Default.png" Height="40"></Image>
<TextBlock Text="{Binding Path=ScreenNumber}" FontSize="11"></TextBlock>
</StackPanel>
</ToggleButton>
</StackPanel>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
the below is my xaml code in xaml page
<ListBox x:Name="ScreensList" ItemsSource="{Binding ScreenCollection}"
SelectedItem="{Binding Path=ScreenManager}"
Style="{StaticResource ScreensList}" Width="365">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel FlowDirection="LeftToRight" Orientation="Horizontal" >
</WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
the following is the viewmodel.cs
public ObservableCollection<ScreensManager> ScreenCollection { get; set; }
private ScreensManager _screenManager;
public ScreensManager ScreenManager
{
get { return _screenManager; }
set
{
if (_screenManager != value)
{
if (_screenManager != null)
{
_screenManager = value;
}
}
}
}
private void AddScreens()
{
int screenCount = Screen.AllScreens.Length;
if (ScreenCollection == null)
ScreenCollection = new ObservableCollection<ScreensManager>();
for (int screenCounter = 1; screenCounter <= screenCount; screenCounter++)
{
if (screenCounter == 1)
{
_screenManager = new ScreensManager();
_screenManager.ScreenNumber = screenCounter;
ScreenCollection.Add(_screenManager);
}
}
}
the following is the code in my ScreenManager.cs model class file
public ScreensManager()
{
}
private int _screenNumber;
public int ScreenNumber
{
get { return _screenNumber; }
set
{
_screenNumber = value;
OnPropertyChanged("ScreenNumber");
}
}
private bool _selectedScreen;
public bool SelectedScreen
{
get { return _selectedScreen; }
set
{
if (_selectedScreen = value)
{
_selectedScreen = value;
if (_selectedScreen != value)
{
OnPropertyChanged("SelectedScreen");
}
}
}
}
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
Am unable to find where I am actually going wrong as completely new to mvvm, someone please help me resolve my issue..Thanks in advance.
Your SelectedScreen is of type bool, and should be of type ScreensManager
Your ListBox.ItemsSource is bound to an ObservableCollection<ScreensManager>, meaning your ListBox contains a collection of ScreensManager objects, however your SelectedItem is of type bool. A bool object is never equal to a ScreensManager object, so WPF doesn't select anything since the SelectedItem is not found in the ItemsSource.
Change the SelectedScreen type to be ScreensManager instead of bool, and be sure it is equal to an item that exists in the ScreenCollection. WPF compares objects by reference, not value, so
ScreenManager.SelectedScreen = ScreenCollection.FirstOrDefault(); // Works
ScreenManager.SelectedScreen = new ScreensManager() { ... }; // Won't work
Make sure the binding is in two-way mode:
SelectedItem="{Binding Path=ScreenManager.SelectedScreen, Mode=TwoWay}"

Why does ComboBox not initially show the ComboBoxItem with IsSelected==true?

In the following code, why doesn't the item with property IsSelected set to true become selected in the ComboBox just as it does in the ListBox after clicking the Button?
Once I click on the ComboBox, the selected item becomes selected, but not before.
xaml:
<Window x:Class="WpfApplication1.Desktop.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<StackPanel>
<ListBox ItemsSource="{Binding Items}">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Txt}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Txt}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Select second item" Click="Button_Click" />
</StackPanel>
</Window>
xaml.cs:
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Windows;
using Microsoft.Practices.Prism.ViewModel;
namespace WpfApplication1.Desktop
{
[Export]
public partial class Shell : Window
{
public class Foo : NotificationObject
{
static int _seq = 0;
string _txt = "Item " + (++_seq).ToString();
public string Txt { get { return _txt; } }
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged(() => IsSelected);
}
}
}
public ObservableCollection<Foo> Items { get; set; }
public Shell()
{
Items = new ObservableCollection<Foo>();
for (int i = 0; i < 5; i++)
Items.Add(new Foo());
DataContext = this;
InitializeComponent();
}
void Button_Click(object sender, RoutedEventArgs e)
{
Items[1].IsSelected = true;
}
}
}
It's because the ItemContainerStyle is applied only when the ComboBoxItems are generated (i.e. when you open the dropdown).
To work around this, you create another property called SelectedItem and bind the Combobox's SelectedValue to it.
Long explanation and example here
Because the binding is set on UpdateSourceTrigger=LostFocus by default, you would have to change it to PropertyChanged to get the result you want.
Like this:
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"Value="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</Style>
When using a Model as the DataContext for a WPF Window, the Controls may not initially behave as you'd expect. Essentially, some properties/events never get set/called until after the Window been initialized. The work-around in this case is to setup the binding in the Window's Loaded event.
Disclaimer: I have not tested this with the OP's specific scenario, but this is the behavior and work-around I've encountered in the past.

Categories