I'm facing a problem where by setting the content property of my window I obviously remove pre-existing content. On all windows I have a dockpanel that I use to pop up help contextual help to the user but this is lost when I set the content property of the window. Therefore I will only see the content for the control I've added and pressing F1 does nothing as the dockpanel does not exist. I don't want to add this dockpanel to every control as it's poor code-reuse so what can I do to keep the dockpanel on the window and add content without overwriting original content of the window?
This is the code where I set the content of the window.
private void btnHelp_Click(object sender, RibbonControlEventArgs e)
{
System.Windows.Window window = new ResizeableWindow()
{
Title = "Help",
Content = new Controls.Help(),
ResizeMode = ResizeMode.NoResize
};
window.ShowDialog();
}
This is code for my Help control it's just a document viewer to read an xps document, this is used by the dockpanel.
public partial class Help : UserControl
{
public Help()
{
InitializeComponent();
string appPath = "path";
XpsDocument doc = new XpsDocument(appPath, FileAccess.Read);
var docx = doc.GetFixedDocumentSequence();
HelpDocViewer.Document = docx;
}
}
This is the xaml of my ResizableWindow containing the Dockpanel
<Window x:Class="Controls.ResizeableWindow"
KeyDown="HelpKeyListen">
<Grid>
<DockPanel x:Name="HelpPanel">
</DockPanel>
</Grid>
</Window>
Here is the code for the resizeable window
public ResizeableWindow()
{
InitializeComponent();
}
private void HelpKeyListen(object sender, KeyEventArgs e)
{
if (e.Key == Key.F1)
{
var HelpControl = new Help();
DockPanel.SetDock(HelpControl, Dock.Right);
HelpPanel.Children.Insert(0, HelpControl);
}
}
Use Placeholders inside the DockPanel instead of replacing the window content:
<DockPanel x:Name="HelpPanel">
<ContentControl x:Name="HelpContent" DockPanel.Dock="Right"/>
<ContentControl x:Name="MainContent"/>
</DockPanel>
Then assign the contents of the contentcontrols as needed
private void HelpKeyListen(object sender, KeyEventArgs e)
{
if (e.Key == Key.F1)
{
HelpContent.Content = new Help();
}
}
Possibly create a new dependency property in ResizeableWindow if you want to provide main content from the outside. Lets say you add a dependency property (visual studio code snipped propdp) named MainContent, then you can bind it as follows:
<DockPanel x:Name="HelpPanel">
<ContentControl x:Name="HelpContent" DockPanel.Dock="Right"/>
<ContentControl x:Name="MainContentPlaceholder" Content="{Binding MainContent,RelativeSource={RelativeSource AnchestorType=Window}}"/>
</DockPanel>
The more appropriate option would be to replace the MainContentPlaceholder by some more WPF/MVVM friendly way to display your contents, but thats out of scope for the question.
Related
I want to view TabView in title bar like image below.
I'm using UWP WinUI 2.
I'm trying to view my TabView in title bar. But TabView is viewing under title bar.
My MainPage.xaml code:
<muxc:TabView Grid.Row="2">
<muxc:TabViewItem Header="New Tab">
</muxc:TabViewItem>
</muxc:TabView>
This is actually pretty easy:
In your xaml code:
This piece of code adds a ShellTitlebarInset and a CustomDragRegion to the TabView. This is needed, to add a margin to the left and right side of the window.
<muxc:TabView x:Name="tabView">
<muxc:TabView.TabStripHeader>
<Grid x:Name="ShellTitlebarInset" Background="Transparent" />
</muxc:TabView.TabStripHeader>
<muxc:TabView.TabStripFooter>
<Grid x:Name="CustomDragRegion" MinWidth="188" Loaded="CustomDragRegion_Loaded" Background="Transparent" />
</muxc:TabView.TabStripFooter>
<muxc:TabViewItem Header="Tab1"/>
<muxc:TabViewItem Header="Tab2"/>
<muxc:TabViewItem Header="Tab3"/>
</muxc:TabView>
In your MainPage:
The LayoutMetricsChanged event handles the FlowDirection either from LeftToRight or RightToLeft to add the specific margin to the CustomDragRegion and ShellTitlebarInset.
private void CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
{
if (FlowDirection == FlowDirection.LeftToRight)
{
CustomDragRegion.MinWidth = sender.SystemOverlayRightInset;
ShellTitlebarInset.MinWidth = sender.SystemOverlayLeftInset;
}
else
{
CustomDragRegion.MinWidth = sender.SystemOverlayLeftInset;
ShellTitlebarInset.MinWidth = sender.SystemOverlayRightInset;
}
CustomDragRegion.Height = ShellTitlebarInset.Height = sender.Height;
}
//Make sure to extend the view after the CustomDragRegion loaded, otherwise the tabs may clip under the minimize, maximize and close buttons of the window:
private void CustomDragRegion_Loaded(object sender, RoutedEventArgs e)
{
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged;
Window.Current.SetTitleBar(CustomDragRegion);
}
Here also the official documentation from Microsoft:
https://learn.microsoft.com/en-us/windows/apps/design/controls/tab-view
I am wondering how to switch to a different tab within a tab control.
I have a main window that has a tab control associated with it and it directs to different pages. I want to switch to a tab from an event triggered within a different tab. When I try to use TabControl.SelectedIndex I get the error "An object reference is required to access non-static, method or property 'MainWindow.tabControl'
Here is my code declaring the TabControl from the MainWindow and trying to switch to it from a different tab.
<TabControl Name="tabControl" Margin="0,117,0,0" SelectionChanged="tabControl_SelectionChanged" Background="{x:Null}" BorderBrush="Black">
<TabItem x:Name="tabMO" Header="MO" IsTabStop="False">
<Viewbox x:Name="viewMO" Margin="0,0,0,0" Stretch="Fill" StretchDirection="Both">
<local:ManufacturingOrder x:Name="mo" Height="644" Width="1322"/>
</Viewbox>
</TabItem>
<TabItem x:Name="tabOptimize" Header="Optimize" IsTabStop="False">
<Viewbox x:Name="viewOptimize" Margin="0,0,0,0" Stretch="Fill" StretchDirection="Both">
<local:EngineeringOptimization x:Name="Optimize" Height="644" Width="1600"/>
</Viewbox>
</TabItem>
</TabControl>
private void dataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var cellInfo = dataGrid.SelectedCells[0];
var content = (cellInfo.Column.GetCellContent(cellInfo.Item) as TextBlock).Text;
var r = new Regex("[M][0-9]{6}");
if (r.IsMatch(content.ToString()))
{
MainWindow.tabControl.SelectedIndex = 4;
}
}
I have tried switching this to a private static void and received the same error.
I have also tried the following code, creating an instance of MainWindow, and there is no errors but when I run the code the selected tab doesn't change on the screen. But if I use a MessageBox to view the Selected Index, than I see my changed tab Index.
private void dataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var cellInfo = dataGrid.SelectedCells[0];
var content = (cellInfo.Column.GetCellContent(cellInfo.Item) as TextBlock).Text;
var r = new Regex("[M][0-9]{6}");
if (r.IsMatch(content.ToString()))
{
MainWindow frm = new MainWindow();
frm.tabControl.SelectedIndex = 4;
}
}
It looks like your main problem is that you do not have easy access to your MainWindow and all of its children from within your ManufacturingOrder or EngineeringOptimization UserControls. Which is normal. There are a few ways around this. A simple one, which violates some MVVM principles, (but you're doing that anyway, so I don't think you'll mind) is to retrieve the instance of your MainWindow object:
//Loop through each open window in your current application.
foreach (var Window in App.Current.Windows)
{
//Check if it is the same type as your MainWindow
if (Window.GetType() == typeof(MainWindow))
{
MainWindow mWnd = (MainWindow)Window;
mWnd.tabControl.SelectedIndex = 4;
}
}
Once you retrieve the running instance of your MainWindow, then you have access to all its members. This has been tested as well as possible without access to your specific custom UserControls and instances. But it's a pretty standard problem and solution.
You were on the right track with your last bit of code in your question, but you were creating a 'new' instance of your MainWindow. You have to retrieve the current running instance, not a new instance.
We have BaseDialogView with next xaml code:
<Window x:Class="Test.BaseDialogView"
Height="475"
WindowStartupLocation="CenterOwner"
SizeToContent="Height"
ResizeMode="CanResize"
SizeChanged="Window_SizeChanged">
<ContentControl Content="{Binding ContentPage}" />
</Window>
BaseDialogViewModel class:
public class BaseDialogViewModel : AbstractNotifyPropertyChangedItem
{
private UserControl contentPage;
public UserControl ContentPage
{
get { return this.contentPage; }
set
{
if (this.contentPage != value)
{
this.contentPage = value;
this.RaisePropertyChanged(() => this.ContentPage);
}
}
}
}
The usage is very simple:
BaseDialog dialog = new BaseDialog();
BaseDialogViewModel dialogVm = new BaseDialogViewModel();
dialog.Owner = Application.Current.MainWindow;
dialog.DataContext = dialogVm ;
dialogVm.ContentPage = new ActivationView();
dialogVm.ContentPage.DataContext = new ActivationViewModel();
So basically once you have an instance of BaseDialog, you just set ContentControl (by setting dialog.ContentPage and dialog.ContentPage.DataContext).
ActivationView is very simple. For example:
<UserControl x:Class="Test.ActivationView" d:DesignHeight="400" d:DesignWidth="700" MaxWidth="700">
<Grid> .... what ever you need
</UserControl>
The problem is that different UserControls windows are set, which have different width and height. When the first UserControl is shown it's place in the center of the MainWindow, which is ok. Then each new userControl is shown, but it's not centered. How do I center the BaseDialog window for each usercontrol?
I tried this (BaseDialogView):
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
Window w = sender as Window;
this.Top = (Application.Current.MainWindow.Height - w.ActualHeight) / 2;
}
but does not work ok (Some usercontrols are still not pixel centered). I also tried adding this to BaseDialogView Xaml
<Window .... VerticalAlignment="Center">
but it seems to be working only for initial instance.
First of you should really consider propperly implementing the MVVM pattern.
It will make your live easier, also instead of centering the element manually in the size change event you should set its owner and WindowStartupLocation by using
Window win = new Window();
win.Content = new MyUserControl();
win.Owner = this;
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
Instead of having one window where you keep changing the content i would consider having different windows..but that may vary on your specific case
I am trying to add Tabs to a tab control in WPF but nothing appears on the control at runtime. I have tried following the examples I keep seeing. Right now this is what I have but it isn't working
_myConnection.Open();
SqlDataReader myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
MessageBox.Show(myReader["SectionName"].ToString());
TabItem newTabItem = new TabItem
{
Header = myReader["SectionName"].ToString(),
Name = myReader["SectionID"].ToString()
};
TabMain.Items.Add(newTabItem);
}
_myConnection.Close();
TabMain.SelectedIndex = 0;
You can add tabs dynamically by using the following code.
Add the following code to declare tab control instance globally.
TabControl tbControl;
Now, add the following code to the loaded event of the tab control.
private void tbCtrl_Loaded(object sender, RoutedEventArgs e)
{
tbControl = (sender as TabControl);
}
I have used a button to add new tabs for the existing tab control.
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
TabItem newTabItem = new TabItem
{
Header = "Test",
Name = "Test"
};
tbControl.Items.Add(newTabItem);
}
Following is my tab control xaml view.
<TabControl x:Name="tbCtrl" HorizontalAlignment="Left" Height="270" Margin="54,36,0,0" VerticalAlignment="Top" Width="524" Loaded="tbCtrl_Loaded">
<TabItem Header="Tab - 01">
<Grid Background="#FFE5E5E5">
<Button x:Name="btnAdd" Content="Add New Tab" HorizontalAlignment="Left" Margin="68,95,0,0" VerticalAlignment="Top" Width="109" Height="29" Click="btnAdd_Click"/>
</Grid>
</TabItem>
</TabControl>
Finally, using this you can add any amount of tabs dynamically to the existing tab control.
Hope this fulfill your need.
Perhaps something in your DB values? I just wrote the most trivial of for loops to test, and this works fine (using just a TabControl and OnLoaded event on the XAML):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 1; i <= 3; i++)
{
var item = new TabItem {Header = i.ToString(), Name = $"tab{i}"};
TabMain.Items.Add(item);
}
}
I have several read only RichTextBox's that are used for logging output. Since they're read only they don't seem to automatically scroll when the text is updated. I can use the TextChanged event to force a scroll to end, but is there not simply a way to set a property or something in the XAML so that scrolling happens like normal?
I had googled for your problem and found this post.
In the section "Programming the RichTextBox" author had described about getting the behavior what you had been expecting.
Please check and let me know if it is of any use.
I tried to reproduce your problem and came up with the following solution
<Window x:Class="CheckRichTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="170" Width="300">
<StackPanel>
<RichTextBox Height="100" Name="richTextBox1" IsReadOnly="True" VerticalScrollBarVisibility="Visible"/>
<Button Name="btnAdd" Content="Click me to add text" VerticalAlignment="Bottom" Click="BtnAddClick" />
</StackPanel>
</Window>
The code behind for the same is as below:
using System.Windows;
namespace CheckRichTextBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void BtnAddClick(object sender, RoutedEventArgs e)
{
richTextBox1.AppendText("You had Clicked the button for adding text\n");
richTextBox1.ScrollToEnd();
}
}
}
This solves the problem of autoscroll, please check it and let me know if it is of any help.
I solved this problem using an Interactivity trigger and a very simple action.
The action looks like this:
public class ScrollToBottomAction : TriggerAction<RichTextBox>
{
protected override void Invoke(object parameter)
{
AssociatedObject.ScrollToEnd();
}
}
Then in my XAML I have this:
<RichTextBox IsReadOnly="True" VerticalScrollBarVisibility="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<interactivity:ScrollToBottomAction/>
</i:EventTrigger>
</i:Interaction.Triggers>
</RichTextBox>
I came up with the following solution for wpf richtextbox autoscroll
public partial class MainWindow
{
private bool AutoScroll = true;
public MainWindow()
{
InitializeComponent();
yourRichTextBox.Loaded += (s, e) =>
{
var scrollViewer = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(yourRichTextBox, 0), 0) as ScrollViewer;
scrollViewer.ScrollChanged += (scroller, eScroller) => ScrollViewer_ScrollChanged(scroller, eScroller);
};
}
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
// User scroll event : set or unset autoscroll mode
if (e.Source as ScrollViewer != null && e.ExtentHeightChange == 0)
{ // Content unchanged : user scroll event
if ((e.Source as ScrollViewer).VerticalOffset == (e.Source as ScrollViewer).ScrollableHeight)
{ // Scroll bar is in bottom
// Set autoscroll mode
AutoScroll = true;
}
else
{ // Scroll bar isn't in bottom
// Unset autoscroll mode
AutoScroll = false;
}
}
// Content scroll event : autoscroll eventually
if (AutoScroll && e.ExtentHeightChange != 0 && e.Source as ScrollViewer != null)
{ // Content changed and autoscroll mode set
// Autoscroll
(e.Source as ScrollViewer).ScrollToVerticalOffset((e.Source as ScrollViewer).ExtentHeight);
}
}
}
This is how to do it in C#
Put the RichTextBox in a ScrollViewer like this:
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
scrollViewer.Content = rTextBox;
Then add the scrollViewer to the Grid or whatever you are using.
RootGrid.Children.Add(scrollViewer);
(This is in C# but could all be done in XAML as well.)
Then use C# code like this to make it scroll to the bottom when you add text:
rTextBox.AppendText(str);
scrollViewer.ScrollToEnd();
Hope that helps.
RichTextBox.AppendText("String")
RichTextBox.ScrollToCaret()
When I was adding to RichTextBox.text, ScrollToCaret() does not work.
RichTextBox.text = RichTextBox.text + "String"
RichTextBox.ScrollToCaret()