Window FontSize is not inherited in child control - wpf

In certain cases I have the problem that the FontSize that I set on a WPF window is not inherited to a child control.
It happens, if a custom user control sets its content (e.g. a Label) upon changing the DataContext.
I can reproduce this when putting this UserControl into a new window, then close this window and create a new one having the same UserControl in it (see the following code).
In my complex application it's a custom popup window and a custom UserControl that changes its content if the DataContext is changed. There the font is not inherited upon the first open of the window (so the usercontrol hasn't been in another visual/logical tree until that) but I can't reproduce this in a small test application.
public partial class App : Application
{
// App.xaml: ShutdownMode="OnExplicitShutdown"
private void Application_Startup(object sender, StartupEventArgs e)
{
var testControl = new TestControl();
var w = new Window();
w.FontSize = 40;
w.DataContext = this;
w.Content = testControl; // TestControl.DataContextChanged creates label which has FontSize = 40
w.Show();
w.Close();
w.DataContext = null;
//w.Content = null; // if this is done, the font will be correct (40)
w = null;
w = new Window();
w.FontSize = 40;
w.DataContext = this;
//testControl.DataContext = this; // if this is done, the font will be correct (40)
w.Content = testControl; // TestControl.DataContextChanged creates label with remaining FontSize = 12 (Default)
w.Show();
}
}
public class TestControl : UserControl
{
public TestControl()
{
DataContextChanged += TestControl_DataContextChanged;
}
private void TestControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null) Content = new Label() { Content = "TestControllabel"};
else Content = null;
}
}
I'm not looking for a fix of this example app, but for the reason why the font size is not inherited in this special case, so maybe then I can fix my complex app.
Any thoughts would be useful !
Edit: For now I fixed my application by setting the datacontext of the control before setting it as window content.

Related

Problems implementing ColorAnimation in Ellipse Fill

I am attempting to implement a ColorAnimation on the Fill of an Ellipse, but not having any success. (I am implementing this ColorAnimation in the code-behind of my app's MainWindow, called via the Messenger pattern from the View Model.):
public partial class MainWindow : Window
{
private SolidColorBrush fillBrush = new SolidColorBrush(Colors.Red);
private ColorAnimation ca = new ColorAnimation(Colors.Red, Colors.Yellow, new Duration(TimeSpan.FromMilliseconds(800)));
private Storyboard flashEllipse = new Storyboard();
public MainWindow(MainWindowVM viewModel)
{
DataContext = viewModel;
InitializeComponent();
Messenger.Default.Register<string>(this, StartFlashEllipse, Constants.StartMsg);
Messenger.Default.Register<string>(this, StopFlashEllipse, Constants.StopMsg);
RegisterName("EllipseFillBrush", fillBrush);
Storyboard.SetTargetName(ca, "EllipseFillBrush");
Storyboard.SetTargetProperty(ca, new PropertyPath("(0).(1)", Ellipse.FillProperty, SolidColorBrush.ColorProperty));
flashEllipse.Children.Add(ca);
flashEllipse.RepeatBehavior = RepeatBehavior.Forever;
}
private void StartFlashEllipse(string ellipseTag)
{
Dispatcher.Invoke(DispatcherPriority.Render, (Action)delegate ()
{
Ellipse ellipseToFlash = <code to find Ellipse in Window>;
ellipseToFlash.Fill = new SolidColorBrush(Colors.Blue);
flashEllipse.Begin(ellipseToFlash);
});
}
...
}
I got the PropertyPath code from this StackOverflow question, but I am still getting a similar Exception on the flashEllipse.Begin() line as the OP of this question:
''Fill' property does not point to a DependencyObject in path '(0).(1)'.'
What am I missing here?
You do not need a Storyboard and a complex PropertyPath.
Just directly start an animation of the Color property of the SolidColorBrush:
ellipseToFlash.Fill = new SolidColorBrush(Colors.Blue);
ellipseToFlash.Fill.BeginAnimation(SolidColorBrush.ColorProperty, ca);

How to rebuild logical tree in WPF without showing window

I have a test code that add a TabItem to TabControl.
But when I try to find the TabItem by name, null is return.
I found a solution is show the window, then I can find the TabItem by name.
But when many tests are running, OutOfMemory exception is occurred because many windows are opened.
Is there another solution to rebuild logical tree without showing window?
The following is my test code
[TestMethod]
public void MyTest2()
{
// Arrange
// Initilize a subVM of CMSEditorViewModel type
var subVM = new SubViewModel();
// Initialize a mainVM of CMSEditorMainViewModel type
var mainVM = new MainViewModel();
// Initialize a MainWindow of DynamicCMS.Exe.CMSEditor
var mainWindow = new MyEditor.MainWindow();
mainWindow.DataContext = mainVM;
ContentPresenter presenter = new ContentPresenter();
using (var stream = System.IO.File.OpenRead(CmsPath.DirViewWithBS + "Subscreen.xaml"))
{
DataTemplate template = XamlReader.Load(stream) as DataTemplate;
presenter.ContentTemplate = template;
presenter.Content = subVM;
}
// Create a TabItem of TabControl
TabItem item = new TabItem();
item.Header = "Tab1";
item.Content = presenter;
item.Name = "tab1";
// Get "mainTabControl" TabControl from MainWindow
CustomTabControl tab = CmsUtil.GetControl((Visual)mainWindow.Content, "mainTabControl") as CustomTabControl;
// Add TabItem to TabControl
tab.Items.Add(item);
mainWindow.Show() // After showing window, I can find the TabItemControl
// Act
TabItem tabItem = (TabItem)CmsUtil.GetControl((Visual)mainWindow.Content, "tab1");
// Assert
Assert.IsNotNull(tabItem);
}

How to add tab items to existing tab control from user Control in wpf

I have MainWindow and 2 user controls. In Main Window there is Tab Control which loads User Control if you click on button search in MainWindow. I could add tab items in main Window by this code.
private void search(object sender, RoutedEventArgs e)
{
tc.Visibility = Visibility.Visible; // It is hidden by default
TabItem tab = new TabItem();
tab.Header = "Поиск";
UCSearch c = new UCSearch(); // User Control 1
tab.Content = c;
tc.Items.Add(tab);
}
When User Control 1 is loaded in Tab item. There is Tetxbox and Button in User Control 1.I want to load User Control 2 when is clicking to Button. But I can not get access to Tab Control which is in Main Window from User Control 1. Please Give me direction. Where to dig?
You could use an Extension method to search the VisualTree for a Parent of type TabControl.
e.g.
Extension method:
public static class VisualTreeExtensions
{
public static T FindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
var parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
return FindParent<T>(parentObject);
}
}
In your Button Handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
var tabControl = (sender as Button).FindParent<TabControl>();
tabControl.Items.Add(new TabItem() { Header = "New"});
}
The better and more flexible (but also more complicated) solution would be to notify the participants (here: your Button fires some kind of message that it was clicked, others (your TabControl) listen and react on it (create a new Tab).
This can for example be done with a Mediator pattern or an EventAggregator.

Silverlight Without XAML - Images Will Not Render

I have a Silverlight application in which I'm not using XAML. I have a basic application with the following code in Application_Startup:
private void Application_Startup(object sender, StartupEventArgs e)
{
Grid g = new Grid();
g.Children.Add(new Image { Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("http://sstatic.net/so/img/sprites.png", UriKind.Absolute)) });
this.RootVisual = g;
}
This code will not render the specified image. If however, the App.Xaml file is modified to define the RootVisual in the Xaml the following works:
xaml:
<Application.RootVisual>
<Grid>
</Grid>
</Application.RootVisual>
code:
private void Application_Startup(object sender, StartupEventArgs e)
{
((Grid)this.RootVisual).Children.Add(new Image { Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("http://sstatic.net/so/img/sprites.png", UriKind.Absolute)) });
}
I don't see why one would work and the other not. I have the same behavior using a UserControl as well (using Content instead of Childern of course).
From what I understand, there should be not XAML requirement. Is there something I'm missing?
The difference is in the first case you are setting the RootVisual to be a Grid, but in the second your grid is a child element.
On the MSDN page for the RootVisual property it shows the following example:
this.RootVisual = new Page();
so if you create a Page and then add your Grid to that page it should work.
Page page = new Page();
page.Content = g;
this.RootVisual = page;

WPF showing dialog before main window

How one can show dialog window (e.g. login / options etc.) before the main window?
Here is what I tried (it apparently has once worked, but not anymore):
XAML:
<Application ...
Startup="Application_Startup">
Application:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
Window1 myMainWindow = new Window1();
DialogWindow myDialogWindow = new DialogWindow();
myDialogWindow.ShowDialog();
}
}
Outcome: myDialogWindow is shown first. When it is closed, the Window1 is shown as expected. But as I close Window1 the application does not close at all.
Here's the full solution that worked for me:
In App.xaml, I remove the StartupUri stuff, and add a Startup handler:
<Application x:Class="MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="ApplicationStart">
</Application>
In App.xaml.cs, I define the handler as follows:
public partial class App
{
private void ApplicationStart(object sender, StartupEventArgs e)
{
//Disable shutdown when the dialog closes
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new DialogWindow();
if (dialog.ShowDialog() == true)
{
var mainWindow = new MainWindow(dialog.Data);
//Re-enable normal shutdown mode.
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
else
{
MessageBox.Show("Unable to load data.", "Error", MessageBoxButton.OK);
Current.Shutdown(-1);
}
}
}
Okay apologizes, here is the solution:
My original question worked almost, only one thing to add, remove the StartupUri from the Application XAML and after that add the Show to main window.
That is:
<Application x:Class="DialogBeforeMainWindow.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
Above, StartupUri removed.
Add myMainWindow.Show() too:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
Window1 myMainWindow = new Window1();
DialogWindow myDialogWindow = new DialogWindow();
myDialogWindow.ShowDialog();
myMainWindow.Show();
}
}
WPF sets App.Current.MainWindow to the first window opened. If you have control over the secondary window constructor, just set App.Current.MainWindow = Null there. Once your main window is constructed, it will be assigned to the App.Current.MainWindow property as expected without any intervention.
public partial class TraceWindow : Window
{
public TraceWindow()
{
InitializeComponent();
if (App.Current.MainWindow == this)
{
App.Current.MainWindow = null;
}
}
}
If you don't have access, you can still set MainWindow within the main window's constructor.
If you put Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; into the constructor of the dialog, and add
protected override void OnClosed(EventArgs e) {
base.OnClosed(e);
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
}
into the dialog class, you don't need to worry about making any changes to the default behaviour of the application. This works great if you want to just snap a login screen into an already-existing app without tweaking the startup procedures.
So you want to show one window, then another, but close down the app when that window is closed? You may need to set the ShutdownMode to OnMainWindowClose and set the MainWindow to Window1, along the lines ok:
Window1 myMainWindow = new Window1();
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Application.Current.MainWindow = myMainWindow;
DialogWindow myDialogWindow = new DialogWindow();
myDialogWindow.ShowDialog();
here, do it like this. this will actaully change your main window and will work properly w/o having to change settings of your application object.
make sure to remove the event handler for application startup and to set your StartupUri in your app.xaml file.
public partial class App : Application
{
bool init = false;
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
if (!init)
{
this.MainWindow.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);
init = true;
}
}
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Window toClose = this.MainWindow;
this.MainWindow = new Window2();
this.MainWindow.Show();
}
}
I have the same issue when i need to disloag a login screen before my main window
In you main window cunstructor add these lines
Application.Current.MainWindow = this;
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Resolve the main window or just call var mainWindow = new MainWindow()
Call the loginScreen.Show() or loginScreen.ShowDialog()

Resources