How to close a window from the user control inside? - c#

I've got a WPF program and at some point I open a window:
public object testCommand()
{
Window window = new Window
{
Title = "My User Control Dialog",
Content = new OKMessageBox("Error Message"),
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.NoResize
};
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
window.ShowDialog();
return null;
}
User Control XAML:
<UserControl x:Class="SolidX.Base.MessageBoxes.OKMessageBox"
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:local="clr-namespace:SolidX.Base.MessageBoxes"
xmlns:viewProperties="clr-namespace:AppResources.Properties;assembly=AppResources"
mc:Ignorable="d"
d:DesignHeight="150" d:DesignWidth="400">
<Grid>
<TextBlock x:Name="textMessage" Margin="10"/>
<Grid Height="Auto" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Content="Don't show this again" HorizontalAlignment="Right" Margin="5,5,10,5"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="btnOk" Content="{x:Static viewProperties:Resources.Common_OK}" Height="25" VerticalAlignment="Center" Width="90" Margin="5"/>
</StackPanel>
</Grid>
</Grid>
</UserControl>
User Control Code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SolidX.Base.MessageBoxes
{
/// <summary>
/// Interaction logic for OKMessageBox.xaml
/// </summary>
public partial class OKMessageBox : UserControl
{
public OKMessageBox(string Message)
{
InitializeComponent();
textMessage.Text= Message;
}
}
}
And in my user control, I am trying to have buttons that close the window (currently, setting Button.IsCancel = true; works, but I want this to be a reusable option - essentially making my own this.Close;).
I tried this (as suggested by a couple other StackOverflow answers) in the code-behind for my usercontrol but to no avail (parentWindow is always null):
var parentWindow = Window.GetWindow(this);
parentWindow.Close();

Try this in the UserControl:
xaml
<UserControl .... Loaded="UserControl_Loaded">
<!-- -->
</UserControl>
cs
public UserControl()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
var window = Window.GetWindow(this);
window.Close();
}

In case you are using ViewModel class for your Window, you can set the ViewModel class to your Window instance, and the ViewModel can store it's owner.
For example the ViewModel:
public class CloseConfirmViewModel
{
internal Window Owner { get; set; }
public ICommand CloseCommand { get; private set; } // ICommand to bind it to a Button
public CloseConfirmViewModel()
{
CloseCommand = new RelayCommand<object>(Close);
}
public void Close() // You can make it public in order to call it from codebehind
{
if (Owner == null)
return;
Owner.Close();
}
}
In order to get it work, you have to set the ViewModel class to your Window:
public partial class CloseConfirmWindow : Window
{
public CloseConfirmWindow(CloseConfirmViewModel model)
{
DataContext = model;
InitializeComponent();
model.Owner = this;
}
}
And you are creating a new instance this way:
var model = new CloseConfirmViewModel();
var closeWindow = new CloseConfirmWindow(model);
closeWindow.ShowDialog(); // Hopefully you've added a button which has the ICommand binded
You can bind the Window's CloseCommand to the UserControl's button this way, or if you are not using commands, call the Close() method when the UC's button is being clicked.

None of those approaches worked for me. Most just closed the parent window. I was using an XAML Popup from my main window. Something like.
<DockPanel/>
<Popup x:Name="puMyControl"/>
<Local:UserControl />
</Popup>
</DockPanel>
Elsewhere in my main window code behind, I added:
puMyControl.IsOpen = true;
Inside the UserControl I had a cancel button. Popup's do not have children, therefore I needed to reference the Popup's parent which was the DockPanel.
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
Popup pu = Parent as Popup;
if ( pu != null )
{
DockPanel dp = pu.Parent as DockPanel;
if (dp != null)
{
dp.Children.Remove(pu);
}
}
}
The aha moment was, that I was removing the Popup, not the UserControl. 'this' would refer to the UserControl. As you can see above, I am removing the Popup from the DockPanel. That led to a simpler and more universal solution.
Popup pu = Parent as Popup;
if (pu != null)
{
pu.IsOpen = false;
}

To close modal dialog you should set DialogResult: DialogResult = false;

Related

WPF: How can I reload the Custom Property's Default Value with it's Design-Time-Value from the parent XAML?

I am writing a User Control named "GridAndChart" that can be filled with values at Design-Time just like this:
<local:GridAndChart HorizontalAlignment="Left"
VerticalAlignment="Top" Width="660"
Height="342"
Margin="28,29,0,0"
NameX="Auslastung in %"
NameY="Wirkungsgrad in %"
MinValueX="0"
MaxValueX="100"
MinValueY="0"
MaxValueY="100"
TitleOfChart="Kennlinie Wirkungsgrad"
Xvals='12,323,43,55,65,7'
Yvals='60,99,99,99,99,99'
/>
Most of the Properties you can see there (Like TitleOfChart, Xvals,...) I defined in the Code-Behind-File of my User-Control as Dependency-Properties.
My Problem is that only those Dependency-Properties are displayed correctly in my Test-Window, that are bound in the UserControl-XAML and therefore invoked by the initializeComponent(), like for example TitleOfChart. By displayed correctly I mean: Display of the Design-time-value from the XAML where I include my UserControl. The other dependency properties don't display correctly because they show only their default value.
From my example you will see that the Evaluation of my dummy-calculations will happen with the default values of the Dependency Properties. Not, after that they got their "Design-Time-Values", read out from the Main.xaml.
Thankyou for your help!
EDIT: Reproducible Code (4 Files)
File 1 and 2: The Window that uses the Control and it's Code-Behind
MainWindow.xaml
<Window x:Class="MinimalReproducibleExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MinimalReproducibleExample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:SimpleUserControl Working="I defined this Text at Design time" NotWorking="1,2,3,4"/>
</Grid>
</Window>
MainWindow.xaml.cs (I left it empty)
using ...
namespace MinimalReproducibleExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
File 3 and 4: The User-Control and it's Code-Behind
SimpleUserControl.xaml
<UserControl x:Class="MinimalReproducibleExample.SimpleUserControl"
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:local="clr-namespace:MinimalReproducibleExample"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Name="TextBox1XAML" />
<TextBox Grid.Row="1" Name="TextBox2XAML" />
</Grid>
</UserControl>
SimpleUserControl.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MinimalReproducibleExample
{
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
string notWorking = NotWorking;//just to show that there happens some calculation with the values
int a = Convert.ToInt32(notWorking.ElementAt(0)); //before they are displayed
int b = Convert.ToInt32(notWorking.ElementAt(1));
int c = a + b;
TextBox2XAML.Text = c.ToString();
}
public string Working
{
get { return GetValue(WorkingProperty).ToString(); }
set { SetValue(WorkingProperty, value); }
}
public static readonly DependencyProperty WorkingProperty = DependencyProperty.Register(nameof(Working), typeof(string), typeof(SimpleUserControl), new PropertyMetadata("The default is working.", (s, e) => (s as SimpleUserControl).TextBox1XAML.Text = (string)e.NewValue));
public string NotWorking
{
get { return GetValue(NotWorkingProperty).ToString(); }
set { SetValue(NotWorkingProperty, value); }
}
public static readonly DependencyProperty NotWorkingProperty = DependencyProperty.Register(nameof(NotWorking), typeof(string), typeof(SimpleUserControl), new PropertyMetadata("The default is also working.", (s, e) => (s as SimpleUserControl).NotWorking = (string)e.NewValue));
}
}
In your MCVE if you put the code to the Loaded event handler of your usercontrol, then all will work as expected. See example:
Usercontrol:
public partial class SimpleUserControl : UserControl
{
public string SimpleTxtProp { get; set; } = "AAA";
public SimpleUserControl()
{
//Constructor being called from InitializeComponent() of parent element. All properties are initialized with values from code behind,
//after object creation is finished, it's property will be initialized with values set in parent element for this control.
InitializeComponent();//Content of control being initialized: constructor -> InitializeComponent()
var isAAA = SimpleTxtProp == "AAA";//true
Loaded += (o, e) =>
{
var isBBB = SimpleTxtProp == "BBB"; //true
};
}
}
MainWindow:
<local:SimpleUserControl SimpleTxtProp="BBB"/>
It doesn't matter whether you consider dependency or simple property.
InitializeComponent() of MainWindow does
trigger the instantiation of SimpleUserControl, hence constructor
with default values/values from code behind being called,
then the properties of the created object being set.
So once an istance of "SimpleUserControl" is loaded, it has the values, which was set in XAML of MainWindow.

C# WPF UI unresponsive when performing drag drop using user control

I am stuck with a C# wpf drag drop issue. I have created a very simple project that includes a User Control, a couple of classes to hold the data and a form to host multiple copies of the user control (using a bound ItemsControl). When I drag the control onto the form the drag drop is triggered, the observablecollection is updated but the UI doesn't reflect the change and future events don't seem to be working. Rolling over the add item button doesn't even show the rollover effect. Sure I am doing something stupid but I can't seem to see what it is.
Code below (mainly from Microsoft Example)
SimpleDataClass
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DragDropControl.Model
{
public class SimpleDataClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _groupName = string.Empty;
private ObservableCollection<SimpleSubDataClass> _titles = new ObservableCollection<SimpleSubDataClass>();
public string GroupName
{
get { return _groupName; }
set
{
if (_groupName != value)
{
_groupName = value;
RaisePropertyChangedEvent("GroupName");
}
}
}
public ObservableCollection<SimpleSubDataClass> Titles
{
get { return _titles; }
set
{
if (_titles != value)
{
_titles = value;
RaisePropertyChangedEvent("Titles");
}
}
}
private void RaisePropertyChangedEvent(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
SimpleSubDataClass
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DragDropControl.Model
{
public class SimpleSubDataClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _title = string.Empty;
public string Title
{
get { return _title; }
set
{
if (_title != value)
{
_title = value;
RaisePropertyChanged("Title");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SimpleSubDataClass(string title)
{
Title = title;
}
}
}
DDControl - XAML
<UserControl x:Class="DragDropControl.DDControl"
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:local="clr-namespace:DragDropControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="CurrentControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Name="txtGroupName" Grid.Row="0" Text="{Binding ElementName=CurrentControl, Path=ThisData.GroupName}"/>
<ListBox Name="lstTitles" Grid.Row="1" ItemsSource="{Binding ElementName=CurrentControl, Path=ThisData.Titles}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Name="lblTitle" Content="{Binding Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
DDControl - Code behind
using DragDropControl.Model;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace DragDropControl
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class DDControl : UserControl
{
public static readonly DependencyProperty ThisDataProperty = DependencyProperty.Register( "ThisData",
typeof(SimpleDataClass),
typeof(DDControl),
new PropertyMetadata(new SimpleDataClass()));
public SimpleDataClass ThisData
{
get { return (SimpleDataClass)GetValue(ThisDataProperty); }
set { SetValue(ThisDataProperty, value); }
}
public DDControl()
{
InitializeComponent();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject data = new DataObject(this.ThisData);
DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
}
}
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
base.OnGiveFeedback(e);
if (e.Effects.HasFlag(DragDropEffects.Move))
Mouse.SetCursor(Cursors.Pen);
e.Handled = true;
}
}
}
MainWindow - Xaml
<Window x:Class="DragDropUserControlWithTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DragDropUserControlWithTextBox"
xmlns:ddc="clr-namespace:DragDropControl;assembly=DragDropControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Name="stkMain" Background="Gray" Orientation="Horizontal" Drop="stkMain_Drop" AllowDrop="true">
<ItemsControl Name="icColumns" Background="Red">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Name="stkItemsControlPanel" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ddc:DDControl Background="{x:Null}" ThisData="{Binding}"/><!-- MouseMove="DDControl_MouseMove" GiveFeedback="DDControl_GiveFeedback"/>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Name="btnAddData" Content="Add Data" Click="btnAddData_Click"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
MainWindow - Code behind
using DragDropControl.Model;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
namespace DragDropUserControlWithTextBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<SimpleDataClass> _data = new ObservableCollection<SimpleDataClass>();
public MainWindow()
{
InitializeComponent();
CreateTestData();
}
private void CreateTestData()
{
SimpleDataClass tempSDC1 = new SimpleDataClass();
tempSDC1.GroupName = "First Item";
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_1"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_2"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_3"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_4"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_5"));
SimpleDataClass tempSDC2 = new SimpleDataClass();
tempSDC2.GroupName = "Second Item";
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_1"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_2"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_3"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_4"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_5"));
_data.Add(tempSDC1);
_data.Add(tempSDC2);
this.icColumns.ItemsSource = _data;
}
private void stkMain_Drop(object sender, DragEventArgs e)
{
if (e.Handled == false)
{
if (e.Data.GetDataPresent(typeof(SimpleDataClass)))
{
SimpleDataClass tempData = (SimpleDataClass)e.Data.GetData(typeof(SimpleDataClass));
_data.Add(tempData);
}
e.Effects.HasFlag(DragDropEffects.None);
Mouse.SetCursor(Cursors.Arrow);
e.Handled = true;
}
}
private void btnAddData_Click(object sender, RoutedEventArgs e)
{
SimpleDataClass tempData = new SimpleDataClass();
tempData.GroupName = "Amazing Test";
tempData.Titles.Add(new SimpleSubDataClass("AT_1"));
tempData.Titles.Add(new SimpleSubDataClass("AT_2"));
tempData.Titles.Add(new SimpleSubDataClass("AT_3"));
_data.Add(tempData);
}
}
}
I swapped to using the method described in this WPF tutorial on drag and drop. It still had an issue when you dragged and dropped a user control with a textbox on it (would assume it would be for any control that is hitenabled) where it would create a second instance of the item being dragged but that is pretty easy to work around, just set the enabled state to false when detecting the control is about to be dragged and enable it again when it is dropped. Possibly a hack but one that works.
For drag and drop, create a Behavior and handle the events in the Behavior for the avoiding the UI thread freeze scenarios. Check the below link will be useful for your scenario.
https://www.telerik.com/blogs/adding-drag-and-drop-to-wpf-listboxes-thanks-telerik!

WPF ComboBox not firing SelectionChanged event if placed at the bottom of Word AddIn

I'm developing Office Word AddIn and using WPF controls for user interface inside WinForms UserControl as a container (inside ElementHost control).
The problem I noticed with ComboBox is that it doesn't fire SelectionChanged event if placed at the bottom of AddIn for items near to the bottom of Word application. For instance, I can click and choose first listed item (if lucky), otherwise ComboBox dropdown (popup) will be closed and SelectionChanged event will not be fired. Instead, Word performs some actions like zooming or changing page layout - actions that are located at the right bottom of Word application, if Addin is located at the right side.
The only workaround I found is that I use ComboBox popup 'upsided'. In that case, events are firing well. As I said, this is a workaround, and I would like to see some smarter solution.
P.S.:If I'm using WinForms ComboBox control, placing it to the bottom doesn't cause this issues - SelectedIndexChanged event works as expected.
Thanks
EDIT: I added some very basic sample code.
Sample code: WpfControl.xaml - UI
<UserControl x:Class="WordAddIn.WpfControl" 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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100*"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<ComboBox x:Name="cboItems" Grid.Row="1" Margin="10,10,10,10"
DisplayMemberPath="NAME"
SelectionChanged="cboItems_SelectionChanged"/>
</Grid>
</UserControl>
Sample code: WpfControl.xaml.cs - Code behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WordAddIn
{
/// <summary>
/// Interaction logic for WpfControl.xaml
/// </summary>
public partial class WpfControl : UserControl
{
public class Item
{
public int ID { get; set; }
public string NAME { get; set; }
}
public WpfControl()
{
InitializeComponent();
List<Item> itemList = new List<Item>();
for (int i = 1; i < 11; i++)
{
itemList.Add(new Item { ID = i, NAME = "Item " + i });
}
cboItems.ItemsSource = itemList;
}
private void cboItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
var item = ((object[])(e.AddedItems)).ToList().FirstOrDefault() as Item;
MessageBox.Show("Item: " + item.NAME + " clicked");
e.Handled = true;
}
e.Handled = false;
}
}
}
WpfContrainer that contains element host and WpfControl as a element host child is added as a Addin on button click on the Word Ribbon.
Sample code: RibbonWord.cs
private void btnTest_Click(object sender, RibbonControlEventArgs e)
{
WpfContainer wpfContainer = new WpfContainer();
var wpfContainerPane = Globals.ThisAddIn.CustomTaskPanes.Add(wpfContainer, "AddIn");
wpfContainerPane.Visible = true;
}
I had the same issue. SelectionChanged does not work when dropdown located outside of wpf. Try change your code in this way
Xaml:
<ComboBox x:Name="cboItems"
Grid.Row="1"
Margin="10,10,10,10"
DisplayMemberPath="NAME"
SelectionChanged="cboItems_SelectionChanged"
DropDownOpened="ComboBox_OnDropDownOpened"
DropDownClosed="ComboBox_OnDropDownClosed" />
Code behind:
namespace WordAddIn
{
public partial class WpfControl : UserControl
{
// your code
private DispatcherFrame dispatcherFrame;
private object DispatcherOperationBegin(object arg)
{
dispatcherFrame = new DispatcherFrame();
Dispatcher.PushFrame(dispatcherFrame);
return null;
}
private void ComboBox_OnDropDownOpened(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(DispatcherOperationBegin), null);
}
private void ComboBox_OnDropDownClosed(object sender, EventArgs e)
{
dispatcherFrame.Continue = false;
}
}
}
More information about DispatcherFrame you can find here.

IsVisibleChanged is not triggered when TabItem visibility is programatically updated

I have a tab control with three tabs, two of which are have Visibility set to collapsed when the program starts, becomes visible under certain conditions, and can later become collapsed again. If the TabItem being collapsed is the currently selected Tab, it's content remains visible even though it has become collapsed.
The tabs' visibility is bound to my ViewModel, and is updated that way.
It will always be the case that I want the first tab to be activated when any of the tab's visibility changes. I've tried to make a simple code behind to handle this case, but the only time that code is hit is when my UserControl is loaded/unloads. The Handler is never invoked when the tab's visibility is updated. I tried setting the IsVisibleChanged property on both the tabcontrol and its items, but I can't get the codebeind to hit.
Here's my xaml:
<UserControl x:Class="MyNameSpace.MyControl"
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"
IsVisibleChanged="TabControl_IsVisibleChanged>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Platform.Presentation;component/Themes/MyTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<TabControl x:Name="_tabControl" IsVisibleChanged="TabControl_IsVisibleChanged">
<TabItem Header="View 1" x:Name="_view1Tab" IsVisibleChanged="TabControl_IsVisibleChanged">
<local:SingleWorkspaceView/>
</TabItem>
<TabItem Header="View 2" x:Name="_view2Tab" Visibility="{Binding TabVisibility}" IsVisibleChanged="TabControl_IsVisibleChanged">
<local:WorkspaceDeploymentView/>
</TabItem>
<TabItem Header="View 3" x:Name="_view3Tab" Visibility="{Binding TabVisibility}" IsVisibleChanged="TabControl_IsVisibleChanged">
<local:TabDeploymentView/>
</TabItem>
</TabControl>
Here's my code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MyNameSpace
{
/// <summary>
/// Interaction logic for TopLevelControl.xaml
/// </summary>
public partial class TopLevelControl : UserControl
{
ApplicationViewModel _viewModel;
public TopLevelControl()
{
_viewModel = new ApplicationViewModel();
base.DataContext = _viewModel;
InitializeComponent();
}
private void TabControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
TabItem tab = sender as TabItem;
if(tab != null && (bool)e.NewValue)
{
_tabControl.SelectedIndex = 0;
}
}
}
}
Is there some reason the event is not firing?
Lets do it in MVVM way without any codebehind
xaml here we have binded SelectedIndex="{Binding TabSelectedIndex}" of TabControl
<TabControl SelectedIndex="{Binding TabSelectedIndex}">
<TabItem Header="abc">
<Button Content="ok"/>
</TabItem>
<TabItem Header="xyz" Visibility="{Binding TabVisibility}">
<Button Content="ok"/>
</TabItem>
<TabItem Header="pqr" Visibility="{Binding TabVisibility}">
<Button Content="ok"/>
</TabItem>
</TabControl>
xaml.cs
public MainWindow()
{
InitializeComponent();
DataContext =new ViewModel();
}
ViewModel
public class ViewModel: INotifyPropertyChanged
{
int tabSelectedIndex;
//this will be bound to the SelectedIndex of Tabcontrol
public int TabSelectedIndex
{
get { return tabSelectedIndex; }
set { tabSelectedIndex = value;
Notify("TabSelectedIndex");
}
}
Visibility tabVisibility;
//this will be binded to the Visibility of TabItem
public Visibility TabVisibility
{
get { return tabVisibility; }
set
{
tabVisibility = value;
//this is the logic that will set firstTab selected when Visibility will be collapsed
if (tabVisibility == Visibility.Collapsed)
{
tabSelectedIndex = 0;
Notify("TabSelectedIndex");
}
Notify("TabVisibility"); }
}
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
I hope this will help. If it will help then say MVVM Rocks :)
This works for me:
private void TabItem_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is TabItem tabItem && tabItem.Parent is TabControl tabControl && tabItem.Visibility == Visibility.Collapsed && tabControl.SelectedItem == tabItem)
{
tabControl.SelectedItem = tabControl.Items.Cast<TabItem>().FirstOrDefault(t => t.Visibility == Visibility.Visible);
}
}

How can I handle a shortcut key in every WPF window?

I want to open the help file to a page based on some custom logic. How can I handle the user pressing F1 on all of my windows (main window and modal dialogs) ?
I know how to handle F1 in a single window, but can this be done globally, so I don't have to add the same code to all of my windows ?
Below is the test with which I've tried out that F1 does not work on the child window.
Window1.xaml:
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Help"
Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<Grid>
<Button Content="Open a new window"
Click="Button_Click"/>
</Grid>
</Window>
Window1.xaml.cs:
using System.Windows;
using System.Windows.Input;
namespace WpfApplication2
{
partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Help");
}
void Button_Click(object sender, RoutedEventArgs e)
{
new Window().ShowDialog();
}
}
}
I've found the answer on this page.
That is, put this in the main window's constructor for example:
CommandManager.RegisterClassCommandBinding(typeof(Window),
new CommandBinding(ApplicationCommands.Help,
(x, y) => MessageBox.Show("Help")));

Categories

Resources