I want to add a blur effect to my window in some cases so I wrote a custom window style.
-I wrote a custom style because I have buttons in front of the blur effect that will get visible when the blur effect is applied, but the buttons should not get blurred.
I used the following code to apply blur:
<AdornerDecorator.Effect>
<BlurEffect Radius="{Binding Path=(local:StandardWindowEventHandler.LockedOverlayVisibility),
Converter={StaticResource VisibilityToBlurConverter}}"
KernelType="Gaussian" />
</AdornerDecorator.Effect>
this worked fine but the GPU is at ~50% when I'm in my TreeView even when radius is 0.
Without blur effect it is at like 2%. Now I don't want to set the radius any more, but the whole blur effect instead.
I have tried this:
<AdornerDecorator.Triggers>
<DataTrigger Binding="{Binding Path=(local:StandardWindowEventHandler.LockedOverlayVisibility)}"
Value="Visible">
<Setter TargetName="PART_WindowAdornerDecorator"
Property="Effect">
<Setter.Value>
<BlurEffect Radius="10" />
</Setter.Value>
</Setter>
</DataTrigger>
</AdornerDecorator.Triggers>
Unfortunately Triggers shall be Event triggers. If I make the event trigger change the radius I've won nothing, is there any possibility to add the blur effect via event trigger?
Thanks in advance
I've found a solution:
<Grid x:Name="Root_Grid"
VerticalAlignment="Stretch">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click"
SourceName="Button1">
<window:SetBlurEffectAction />
</i:EventTrigger>
<i:EventTrigger EventName="Click"
SourceName="Button2">
<window:SetBlurEffectAction />
</i:EventTrigger>
</i:Interaction.Triggers>
<AdornerDecorator ClipToBounds="True"
x:Name="PART_WindowAdornerDecorator">
<ContentPresenter x:Name="PART_RootContentPresenter"
ContentTemplate="{TemplateBinding ActualWindowTemplate}"
dxr:RibbonControlHelper.IsAutoHide="{TemplateBinding RibbonAutoHideMode}"
dxr:RibbonControlHelper.DisplayShowModeSelector="{TemplateBinding DisplayShowModeSelector}" />
</AdornerDecorator>
The Root_Grid is the parent of the AdornerDecorator as you can see.
Button1 and Button2 are some grandchildren of the Root_Grid.
public class SetBlurEffectAction : TriggerAction<DependencyObject> {
protected override void Invoke(object parameter) {
var e = parameter as RoutedEventArgs;
// OriginalSource is one of the buttons
var parent = e?.OriginalSource as UIElement;
// get Grid (parent of AdornerDecorator)
while(true) {
if(parent == null) {
return;
}
var grid = parent as Grid;
if("Root_Grid".Equals(grid?.Name)) {
break;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
// apply blur to this AdornerDecorator
var deco = ((Grid)parent).GetElementByName("PART_WindowAdornerDecorator") as AdornerDecorator;
if(deco == null) {
return;
}
// != collapsed because the property is not updated yet
if(StandardWindowEventHandler.LockedOverlayVisibility != Visibility.Collapsed) {
deco.Effect = null;
} else {
deco.Effect = new BlurEffect {
KernelType = KernelType.Gaussian,
Radius = 7
};
}
}
}
StandardWindowEventHandler.LockedOverlayVisibility is a static Property that gets updated when one of the buttons is pressed.
Related
I am new to MVVM and I am currently trying to add the drag/drop feature to my application. The thing is I already developed the interface in the code-behind but I am trying now to re-write the code into MVVM as I am only at the beginning of the project.
Here is the context: the user will be able to add boxes (ToggleButton but it may change) to a grid, a bit like a chessboard. Below is the View Model I am working on:
<Page.Resources>
<Style TargetType="{x:Type local:AirportEditionPage}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Page}">
<!-- The page content-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ToolKitWidth, FallbackValue=50}" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="{Binding RightPanelWidth, FallbackValue=400}"/>
</Grid.ColumnDefinitions>
<!-- The airport grid where Steps and Links are displayed -->
<ScrollViewer Grid.ColumnSpan="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Viewbox Height="{Binding AirportGridHeight}" Width="{Binding AirportGridWidth}" RenderOptions.BitmapScalingMode="HighQuality">
<ItemsControl x:Name="ChessBoard" ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="{Binding CardQuantityRow}" Height="{Binding CardQuantityColumn}" Background="{StaticResource AirportGridBackground}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="1" Height="1">
<ToggleButton Style="{StaticResource StepCardContentStyle}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Pos.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Pos.Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Viewbox>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
Items are basically from a class (child of INotifiedPropertyChanged) with a name, an icon and a position (Point).
Now, I am trying to make the user able to drag and drop the box (ToggleButton) within the grid wherever he/she wants. However, I am totally lost with Commands, AttachedProperties etc. I spent all the whole day on tutorials and tried drag/drop solutions but with my poor knowledge, I don't know how to apply all of this into my code.
On my code-behinded version of the code, it was easy. When the button is left-clicked, I say to a variable of the grid "hey, I am being dragged and dropped". While the user is moving, I changed the Item coordinates and when the user released the left button (left button up), the dragdrop_object variable comes null again.
In the frame of the MVVM, I am totally lost. Could you give me some tracks to help me trough ? I intended to give up with MVVM a lot of time, but I know that it is better to keep up even if every little feature takes litteraly hours for me to implement (it should decrease with time...).
Do not hesitate if you need further details to answer to my question.
I found the solution here : Move items in a canvas using MVVM and here : Combining ItemsControl with draggable items - Element.parent always null
To be precise, here is the code I added :
public class DragBehavior
{
public readonly TranslateTransform Transform = new TranslateTransform();
private static DragBehavior _instance = new DragBehavior();
public static DragBehavior Instance
{
get { return _instance; }
set { _instance = value; }
}
public static bool GetDrag(DependencyObject obj)
{
return (bool)obj.GetValue(IsDragProperty);
}
public static void SetDrag(DependencyObject obj, bool value)
{
obj.SetValue(IsDragProperty, value);
}
public static readonly DependencyProperty IsDragProperty =
DependencyProperty.RegisterAttached("Drag",
typeof(bool), typeof(DragBehavior),
new PropertyMetadata(false, OnDragChanged));
private static void OnDragChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// ignoring error checking
var element = (UIElement)sender;
var isDrag = (bool)(e.NewValue);
Instance = new DragBehavior();
((UIElement)sender).RenderTransform = Instance.Transform;
if (isDrag)
{
element.MouseLeftButtonDown += Instance.ElementOnMouseLeftButtonDown;
element.MouseLeftButtonUp += Instance.ElementOnMouseLeftButtonUp;
element.MouseMove += Instance.ElementOnMouseMove;
}
else
{
element.MouseLeftButtonDown -= Instance.ElementOnMouseLeftButtonDown;
element.MouseLeftButtonUp -= Instance.ElementOnMouseLeftButtonUp;
element.MouseMove -= Instance.ElementOnMouseMove;
}
}
private void ElementOnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
((UIElement)sender).CaptureMouse();
}
private void ElementOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
((UIElement)sender).ReleaseMouseCapture();
}
private void ElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
FrameworkElement element = sender as FrameworkElement;
Canvas parent = element.FindAncestor<Canvas>();
var mousePos = mouseEventArgs.GetPosition(parent);
if (!((UIElement)sender).IsMouseCaptured) return;
if (mousePos.X < parent.Width && mousePos.Y < parent.Height && mousePos.X >= 0 && mousePos.Y >=0)
((sender as FrameworkElement).DataContext as Step).Pos = new System.Drawing.Point(Convert.ToInt32(Math.Floor(mousePos.X)), Convert.ToInt32((Math.Floor(mousePos.Y))));
}
}
And my DataTemplate is now:
<DataTemplate>
<ContentControl Height="1" Width="1" local:DragBehavior.Drag="True" Style="{StaticResource StepCardContentControl}"/>
</DataTemplate>
I added the FindAncestor static class in a dedicated file like this:
public static class FindAncestorHelper
{
public static T FindAncestor<T>(this DependencyObject obj)
where T : DependencyObject
{
DependencyObject tmp = VisualTreeHelper.GetParent(obj);
while (tmp != null && !(tmp is T))
{
tmp = VisualTreeHelper.GetParent(tmp);
}
return tmp as T;
}
}
(My items are now ContentControls).
As the items' positions within the canvas are directly managed with their Pos variable (Canvas.SetLeft and Canvas.SetTop based on Pos (Pos.X and Pos.Y) with Binding), I just update it according to the MousePosition within the Canvas.
Also, as suggested in a commentary, I will see if there is something better than the ScrollViewer and Viewbox I'm using.
I want to increase the size of a TextBox Control whenever the user drag a node from Treeview control and hovers the mouse over the TextBox.
The size increase should not readjust the other controls, rather the current control should overlap the neighboring controls.
I tried to implement the code WPF: On Mouse hover on a particular control, increase its size and overlap on the other controls
but it doesn't work when hover on TextBox and left mouse button is pressed for dragged text.
<ItemsControl Margin="50">
<ItemsControl.Resources>
<Style x:Key="ScaleStyle" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Grid.ZIndex" Value="1"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="1.1" ScaleY="1.1"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.Resources>
</ItemsControl>
Here is a small sample application. Contrary to my comment, we need the PreviewDragEnter event since the text box already has Drag/Drop support. In Window_Loaded, the application registers the event handlers. Then, in TextBox_PreviewDragEnter, the new style is set manually. We also store the old z-index to allow restoring it in TextBox_PreviewDragLeave.
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525" Loaded="Window_Loaded">
<StackPanel Margin="8">
<TextBox/>
<TextBox/>
<TextBox/>
<TextBox/>
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//From https://stackoverflow.com/a/978352/1210053
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
foreach (var txt in FindVisualChildren<TextBox>(this))
{
txt.PreviewDragEnter += TextBox_PreviewDragEnter;
txt.PreviewDragLeave += TextBox_PreviewDragLeave;
txt.PreviewDrop += TextBox_PreviewDragLeave;
}
}
private Dictionary<TextBox, int> oldZIndex = new Dictionary<TextBox, int>();
private void TextBox_PreviewDragEnter(object sender, DragEventArgs e)
{
var txt = (TextBox)sender;
oldZIndex.Add(txt, Panel.GetZIndex(txt));
Panel.SetZIndex(txt, 1);
var scaleTransform = new ScaleTransform(1.1, 1.1, txt.ActualWidth / 2, txt.ActualHeight / 2);
txt.RenderTransform = scaleTransform;
}
private void TextBox_PreviewDragLeave(object sender, DragEventArgs e)
{
var txt = (TextBox)sender;
txt.RenderTransform = null;
Panel.SetZIndex(txt, oldZIndex[txt]);
oldZIndex.Remove(txt);
}
}
Approach from a different angle. Use the code behind to handle left click and drag.
Pseudo code...
If hover over textbox.text ==true
Textbox size = 300;
Then check the Grid location of the textbox. It should be allowed to columnspan over the other columns, while the rest of the controls stay fixed in their grid.row and grid.column locations.
I have five images, when you click one of them I want that one to get full opacity while the other only gets half, to show it is the selected one.
I am using MVVM and generally wondering if I'm doing it the right way
I was thinking about passing the name of the imagesource binded into a property.
<StackLayout Grid.Row="3" Grid.Column="1" Orientation="Horizontal" Spacing="0">
<Image Source="{Binding StatusUnresolved}" HorizontalOptions="Center"
VerticalOptions="Center" HeightRequest="40" Opacity="{Binding StatusUnresolvedOpacity}">
<Image.GestureRecognizers>
<!--<TapGestureRecognizer Command="{Binding Source={x:Reference this}, Path=OnStatusTappedCommand}" CommandParameter="{Binding StatusUnresolved}" />-->
</Image.GestureRecognizers>
</Image>
</StackLayout>
The list that turns the string into status later on.
public List<IssueStatusModel> PossibleStatusValues
{
get
{
var items = new List<IssueStatusModel>
{
new IssueStatusModel("statusUnresolved.png", IssueStatus.Unresolved),
new IssueStatusModel("statusInProgress.png", IssueStatus.InProgress),
new IssueStatusModel("statusDone.png", IssueStatus.Done)
};
return items;
}
}
Property for opacity
public double StatusDoneOpacity
{
get { return statusDoneOpacity; }
set
{
if (statusDoneOpacity != value)
{
statusDoneOpacity = value;
NotifyPropertyChanged(nameof(StatusUnresolvedOpacity));
}
}
}
public string StatusDone
{
get { return "statusDone.png"; }
}
public void OnStatusTapped(string fileName)
{
foreach (IssueStatusModel item in StatusValues)
{
if (item.Name != fileName) continue;
Issue.Status = item.Status;
StatusChecker();
return;
}
}
}
Switch statement Changing all the opacities.
private void StatusChecker()
{
switch (Issue.Status)
{
case IssueStatus.Unresolved:
StatusUnresolvedOpacity = 1;
StatusInProgressOpacity = 0.5;
StatusDoneOpacity = 0.5;
StatusText = "Unresolved";
break;
case IssueStatus.InProgress:
StatusUnresolvedOpacity = 0.5;
StatusInProgressOpacity = 1;
StatusDoneOpacity = 0.5;
StatusText = "In Progress";
break;
case IssueStatus.Done:
StatusUnresolvedOpacity = 0.5;
StatusInProgressOpacity = 0.5;
statusDoneOpacity = 1;
StatusText = "Done";
break;
}
}
The way I'd attack this, if you have multiple images, create an ImageVm and encapsulate any image specific implementation details i.e. enum State and an IsSelected notification properties. Of course if you only have 1 image this becomes trivially easy and you don't need vms
Use a DataTrigger that binds to an IsSelected MVVM property to set the Opacity and state if you need to change the image source. Obviously on click you will need to set the IsSelected Property and deselect the other VMs
Example of DataTrigger for IsSelected
<Image Grid.Column="2" Stretch="None">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Opacity" Value="0.5" />
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsSelected}">
<Setter Property="Opacity" Value="0.5"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Update
You CAN use triggers with enums, and you can use a tap recognizers to fire commands in your main viewmodals. also commands can take parameters as well.
It's probably better (knowing what you have described in the comments) to just make a State and Severity enum and bind to it, and set the State and Severity via a command by a gesture.
Then you could just make a Trigger for each Image to change the Opacity for each image on the various state and severity.
First, the heart of the question: If an element is assigned as the Content of a ContentControl via a style trigger, I can't seem to find it by name.
Now, for more detail: I have a panel that varies greatly in its layout and functionality based on its data context, which is a bug from a bug depot. When that bug is null, it is a search form, when it is non-null, it is a simple viewer for properties of that bug. The XAML then look something like:
<ContentControl DataContext="...">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content">
<Setter.Value>
...
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter Property="Content">
<StackPanel>
<TextBox Name="Waldo"/>
<Button .../>
</StackPanel>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
When the user clicks the button that sits alongside the text box, I get a callback in the code behind. From that point I'd like to be able to access various properties of the text box. The question is, where's Waldo? :)
In the code behind I have tried a few variants of the following, all with little success:
this.FindName("Waldo"); // Always returns null
I've seen a lot of discussion on this topic as it relates to templates but not as it relates to setting content directly with triggers. Maybe it's because I am violating all sorts of best practices by doing this :)
Thank you!
If an element is assigned as the Content of a ContentControl via a style trigger, I can't seem to find it by name.
If you needed to access to the Content before trigger occurs, it would most likely not possible. In this situation, the main thing to get access after the DataTrigger occurs.
I am violating all sorts of best practices by doing this
Maybe it's not the right way to work with the Сontrol in WPF, the more that you still need access to dynamic content, which can later be changed. But in any case, there are two ways to work with the Сontrol - it's like now and in the MVVM style. MVVM style is best suited for large and less complex applications with different business logic. If in your case for easy application, in this situation, I do not see anything wrong with that. In addition to doing a project in MVVM style need from scratch, combine conventional method and the correct method is not a good way.
I created a small example to demonstrate access controls for a given situation. There is a property that corresponds to the type of Content, the default is Init. If you assigns null for this property, the dynamic Content is loaded.
That's how I get access to TextBox:
private void GetAccessToTextBox_Click(object sender, RoutedEventArgs e)
{
TextBox MyTextBox = null;
StackPanel panel = MainContentControl.Content as StackPanel;
foreach (object child in panel.Children)
{
if (child is TextBox)
{
MyTextBox = child as TextBox;
}
}
if (MyTextBox != null)
{
MyTextBox.Background = Brushes.Gainsboro;
MyTextBox.Height = 100;
MyTextBox.Text = "Got access to me!";
}
}
Below it's a full example:
XAML
<Window x:Class="AccessToElementInContentControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:AccessToElementInContentControl"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<this:TestData />
</Window.DataContext>
<Window.Resources>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content">
<Setter.Value>
<Label Content="InitContent"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=TypeContent}" Value="{x:Null}">
<Setter Property="Content">
<Setter.Value>
<StackPanel Name="NullStackPanel">
<TextBox Name="Waldo" Text="DynamicText" />
<Button Width="100" Height="30" Content="DynamicButton" />
</StackPanel>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ContentControl Name="MainContentControl" />
<Button Name="SetContentType"
Width="100"
Height="30"
HorizontalAlignment="Left"
Content="SetContentType"
Click="SetContentType_Click" />
<Button Name="GetAccessToButton"
Width="110"
Height="30"
HorizontalAlignment="Right"
Content="GetAccessToTextBox"
Click="GetAccessToTextBox_Click" />
</Grid>
</Window>
Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SetContentType_Click(object sender, RoutedEventArgs e)
{
TestData test = this.DataContext as TestData;
test.TypeContent = null;
}
private void GetAccessToTextBox_Click(object sender, RoutedEventArgs e)
{
TextBox MyTextBox = null;
StackPanel panel = MainContentControl.Content as StackPanel;
foreach (object child in panel.Children)
{
if (child is TextBox)
{
MyTextBox = child as TextBox;
}
}
if (MyTextBox != null)
{
MyTextBox.Background = Brushes.Gainsboro;
MyTextBox.Height = 100;
MyTextBox.Text = "Got access to me!";
}
}
}
public class TestData : NotificationObject
{
private string _typeContent = "Init";
public string TypeContent
{
get
{
return _typeContent;
}
set
{
_typeContent = value;
NotifyPropertyChanged("TypeContent");
}
}
}
public class NotificationObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I've got following code:
private Dictionary<int, UserControl> tabControls = new Dictionary<int, UserControl>();
public MainWindow()
{
InitializeComponent();
tabControls[0] = new Panel1();
tabControls[1] = new Panel2();
tabControls[2] = new Panel3();
tabControls[3] = new Panel4();
tabControls[4] = new Panel5();
tabControls[5] = new Panel6();
tabControls[6] = new Panel7();
tabControls[7] = new Panel8();
}
public object SelectedTab
{
//this is assigned from xaml binding
set
{
OnCurrentTabChanged(tabControl.SelectedIndex);
}
}
void OnCurrentTabChanged(int tabIndex)
{
if (dataDisplay != null)
{
dataDisplay.Children.Clear();
dataDisplay.Children.Add(tabControls[tabIndex]);
}
}
Every time the user selects different tab, an other control appears.
Is there any way to simplify this using xaml?
I cannot put the controls themselves inside the tab control
I've done this before with another TabControl which has it's headers and frame hidden. Then I just bind the SelectedIndex to your other tab's SelectedIndex, and the two are synchronized
<!-- TabControl without the TabHeaders -->
<Style x:Key="TabControl_NoHeadersStyle" TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<DockPanel>
<!-- This is needed to draw TabControls with Bound items -->
<StackPanel IsItemsHost="True" Height="0" Width="0" />
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then you can setup your two tab controls, each bound to different sources, and bind the SelectedIndex of one to the SelectedIndex of the other
<TabControl x:Name="MainTabControl" />
<TabControl ItemsSource="{Binding Panels}"
SelectedIndex="{Binding ElementName=MainTabControl, Path=SelectedIndex}"
Style="{StaticResource TabControl_NoHeadersStyle}" />
Another alternative is to bind the SelectedIndex to something in your code-behind, then anytime it changes, raise a PropertyChanged notification on another property that exposes the panel you want to display.
<TabControl SelectedIndex="{Binding SelectedTabIndex} />
<ContentControl Content="{Binding SelectedPanel}" />
and in the code behind
public int SelectedTabIndex
{
get { return _selectedTabIndex;}
set
{
if (_selectedTabIndex != value)
{
_selectedTabIndex = value;
RaisePropertyChanged("SelectedTabIndex");
RaisePropertyChanged("SelectedPanel");
}
}
}
public UserControl SelectedPanel
{
get { return tabControls[SelectedTabIndex]; }
}
TabItem has an IsSelected propery you could bind to that I think would simplify the syntax.
public bool TabIsSelected
{
get { return tabIsSelected; }
set
{
if (value && dataDisplay != null)
{
dataDisplay.Children.Clear();
dataDisplay.Children.Add(tabControls[tabIndex]);
}
tabIsSelected = value;
}
But I still don't get why you can't just put the control in the tabitem?
using codebehind
void OnCurrentTabChanged(int tabIndex)
{
if (dataDisplay != null)
{
UIElemnt[] pp = dataDisplay.Children.Cast<UIElement>().ToArray();
Array.ForEach(pp, x=> x.visibility = Visibility.Collapsed);
pp[tabIndex].visibility = Visibility.Visible;
}
}