So I got a tabcontrol that is bound to an list(has name and code). And this is working perfectly
<TabControl.ContentTemplate>
<DataTemplate>
<sp:ucercontroltest DataContext="{Binding}" strname="{Binding Path=name}" strcode="{Binding Path=code}" />
</DataTemplate>
</TabControl.ContentTemplate>
But if I would add a Button(btntestbutton) on usercontroltest with an event, that if the button is clicked I want the button to be disabled (btntestbutton.IsEnabled = false) then it gets disabled on ALL the usercontrols(tabs)! How can I prevent it from all being shared wich each other so If I for example want to disable the button on 1 usercontrol so that I don t automaticly disable all of them.
As far as i know TabControls reuse the controls created from the ContentTemplate, one way to have a state unique to the tabs would be to bind the IsEnabled property of the button to a property on the VM of each tab, then the state would adjust on tab-switch. (Of course you then would need to adjust the VM property in the handler, not the IsEnabled)
Related
In my application I'm using a simple view selector using datatemplates, I basically have a content control and datatemplates like
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:OverviewViewModel}">
<local:OverviewView></local:OverviewView>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:DetailViewModel}">
<local:DetailView></local:DetailView>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl Content="{Binding CurrentView}" Grid.Column="0"/>
</Grid>
Simple, efficient and easily extendable. (Key point is that these usercontrol views can be recursive). The main view also holds a few button with commands behind them to go to "another view".
The problem is, the DetailView contains a ListBox (ListView to be exact), at which "selected items" is of actual importance for the application state.
Now I notice that if I switch from the DetailView to the OverviewView the SelectionChanged event fires - actually unselecting all elements. Looking through the callstack this is due to the fact that the actual elements inside the ListView are updated, the listview is being cleared when the datatemplate changes.
Now since this actually changes the application state this is bad. (The user didn't actually click to unselect the elements, the selection is one of the main reasons to actually open the detail view).
I've tried catching the Unloaded event from the ListBox: this event is indeed fired. But also AFTER the selectionchanged event is fired, so it doesn't really help here.
So can I hook up to a "this UserControl will be unloaded momentarily" event? The bigger UserControl (and the ViewModel for that) should be considered a black box from the smaller DetailView/DetailViewModel.
I have a TabControl with a binding for the ItemsSource property. It's bound to a ObservableCollection. When I add an item to the collection, the tabs get created correctly, but only the first tab gets its Grid_Loaded event fired. I'm guessing this is because it's focused. I need to initialize stuff when a new tab opens, it contains a control that needs to be referenced.
<TabControl x:Name="tabSessions"
ItemsSource="{Binding Sessions}"
SelectedIndex="0"
BorderThickness="0"
Padding="0,0,0,0">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<TextBlock Margin="4,4,16,4" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid Loaded="Grid_Loaded">
<!-- View here -->
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Is there an event that I could use to initialize this? I also need the DataContext to be set, and the view to be initialized.c#
The DataTemplate is reused by the tabs, which is why you're probably only seeing one Loaded event. It's only loaded the first time and then reused when you switch tabs. Only the binded content is changed.
Instead of listening to Loaded on the Grid, you could rather listen to DataContextChanged which will tell you every time the DataContext is set to a new object.
The DataContext will change when you switch tabs. The current tab will be set as the DataContext of the DataTemplate (and by extensions the Grid).
When using a DataTemplate you assume that the view is going to be the same, but the content is going to differ. If this is not the case and the views will differ based on the content, you'll probably want to look into using a DataTemplateSelector. This will let you define several DataTemplates and select one of them to use based on the current DataContext. You can read up on DataTemplateSelector in the Microsoft Docs
I have a CheckBox in a TabItem Header in a WPF application. I am finding that when you click on the TabItem, it is checking the CheckBox, but not opening the TabItem. I would like only the actual CheckBox to register the click event to change the state from Checked to Unchecked or vice versa. That way, clicking on the label of the CheckBox will open the TabItem.
I've tried using an empty CheckBox that has no content and then a separate Label adjacent to it, but then the TabItem tells me that it cannot have 2 headers.
<TabItem>
<TabItem.Header>
<CheckBox Name="chkExportEnabled" Content="{x:Static resources:Global.Label_Export}" IsChecked="{Binding EnableExport}"/>
</TabItem.Header>
</TabItem>
You can just use a StackPanel with Orientation='Horizontal' around the label-less CheckBox and the Label. TabItem.Header may only contain a single child, that's why you get the error, but nothing prevents you from using layout containers as that child.
I am building a simple application that mainly consists of a TabControl and a StatusBar with an Slider. Each tab hosts an custom control that can be zoomed in & out. The slider in the status bar should give the user the abillity to zoom in & out the content of the currently selected tab. My problem is that I'am unable to assign the sliders value to the currently selected custom control.
Here's the xaml of my TabControl:
<TabControl x:Name="MyTabControl" ItemsSource="{Binding MyItems}">
<TabControl.ContentTemplate>
<DataTemplate>
<controls:MyControl x:Name="foo" DataContext="{Binding}">
</controls:MyControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
For the slider I want something like this, but it should be bound to the "foo.bar" property of the currently selected tab:
<Slider
Value="{Binding ElementName=foo, Path=bar}">
</Slider>
Is that possible or is there a better way to use a Slider to adjust and display a property of the currently selected custom control?
You can try adding a Value property to the class (or classes) that are set as the DataContext for each TabItem. Then you could data bind the Slider.Value property to the Value property in the data item from the selected TabItem using the TabControl.SelectedItem property. Try this:
<Slider Value="{Binding Path=SelectedItem.Value, ElementName=MyTabControl}" />
I have a WPF interface that has a panel used for displaying details about the particular option you have selected from a button bar. Eg if you click the Info button, the detail pane populates with a InfoDetailUserControl. If you click the Graph button, it populates with a GraphDetailUserControl.
The way I am doing this is to define each detail panel as a UserControl. I then have a ViewModel for each UserControl that drives the data. The detail panel is represented by a ContentControl and to display the relevant panel, I set the content of this to the ViewModel representing the UserControl I want to display. I then have a number of DataTemplates that map a ViewModel to a UserControl, so that when you add the VM to the ContentControl, it looks up the datatemplate for that type and displays the relevant UserControl.
Example datatemplates.
<DataTemplate DataType="{x:Type RunResults:SimpleCalcInfoResultViewModel}">
<Views:SimpleCalcInfoResult />
</DataTemplate>
<DataTemplate DataType="{x:Type RunResults:TradeResultViewModel}">
<Views:TradeResult />
</DataTemplate>
<DataTemplate DataType="{x:Type RunResults:GraphResultViewModel}">
<Views:GraphResult />
</DataTemplate>
<DataTemplate DataType="{x:Type RunResults:NoResultViewModel}">
<Views:NoResult />
</DataTemplate>
This all works fine, but the problem is that every time you change the content of the detail panel, you supply it the ViewModel class and it then on-the-fly looks up the datatemplate that matches the VM type and creates an instance of that UserControl. It then discards that UserControl when you switch to a different type. Hence if you keep switching between Info and Graph view for example, it keeps recreating the GraphUserControl every time you go back to it, it doesn't cache it from the first load and just redisplay the same view again.
The problem I have is that the Graph UserControl takes 3-4 seconds to initialise, in the InitializeComponent() call. I'm assuming this is just the WPF toolkit chart control being slow but it means the user must wait 4 seconds every time they go back to the Graph view, which is not ideal.
Is there a way I can either easily cache the first UserControl created so it only ever goes through that initialisation once or is there a way I can simply speed up the loading of the Chart control?
Many thanks