Object's ActualWidth and ActualHeight reporting as 0? - c#

I have an application that contains several instances of a Grid that inherits from an Abstract class extending UserControl like this:
<abstract:ScoringGrid x:Class="ReadProject.Repeater.ScoringGrid"
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:abstract="clr-namespace:ReadProject.AbstractRepeater"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="ScoringGridInstance" Width="Auto" Height="Auto">
....
and the class they inherit from (which is a very basic class with just a few methods and properties):
public abstract partial class ScoringGrid : UserControl, INotifyPropertyChanged
{...
These grids are built dynamically at runtime and each one can vary in size. When the user closes the application, I want to capture a screenshot of each Grid and save them to the local machine. To do this, I created a method to get the objects size and save it to a file using RenderTargetBitmap(). A lot of them save without issue, however some of the grids are returning an ActualWidth and ActualHeight of 0 and thus not saving. I'm uncertain of where to start debugging this issue since they are all instances of the same exact object; how could it be possible that some of them have their ActualWidth and ActualHeight set and others do not? They all display without issue in the application.
Let me know if you need more information/code. Thanks!

For completeness, I was able resolve the issue by adding these calls to each UserControl before capturing a screenshot of them:
scoreGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
scoreGrid.Arrange(new Rect(0, 0, scoreGrid.ScoreGridInstance.DesiredSize.Width, scoreGrid.ScoreGridInstance.DesiredSize.Height));

Related

Difference between different ways of initializing dataContext in WPF

I have an WPF UserControl in which I try to initialize the DataContext in two ways:
First way
<UserControl x:Class="my.UI.Views.MyControlView"
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:my.UI.Views"
xmlns:vm="clr-namespace:my.UI.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:MyControlViewModel}"
d:DesignHeight="450" d:DesignWidth="800">
I have noticed that using this way I still need to initialize the DataContext in the subyacent code (xaml.cs) in the constructor as following:
private readonly MyControlViewModel viewModel = new MyControlViewModel();
public MyControlView()
{
InitializeComponent();
this.DataContext = this.viewModel;
}
Otherwise, if I don't initialize it in the constructor as well, then when I try to get access to the DataContext by doing the following I get a null:
MyControlViewModel vm = (MyControlViewModel) this.myControlView?.DataContext;
Note:
this.myControlView makes reference to the correct WPF usercontrol. This is embedded in an elementhost.
So in this way I need to initialize the DataContext in two places, in the view (xaml) and also in the subyacent code (xaml.cs). I am wondering, Why do I need to initialize it in both places and not only in one instead?
Second way
<UserControl x:Class="my.UI.Views.MyControlView"
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:my.UI.Views"
xmlns:vm="clr-namespace:my.UI.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:MyControlViewModel/>
</UserControl.DataContext>
Using this second way, initializing the DataContext in the view (xaml) is enough, no need to initialize it again in the subyacent code (xaml.cs). In this case when I do the same, I mean, I try getting access to the DataContext is working:
MyControlViewModel vm = (MyControlViewModel) this.myControlView?.DataContext;
So:
What's the difference between the two ways? Which is best?
In the first way, Why do I need to initialize it in both places and not only in one instead?
d:DataContext is a design time data context, to enable you to visualise your data and page layout.
The code behind example is where it has been set at runtime, but isn't advisable in this form (although it works) as its inflexible in a final product.
Use of dependency Injection is a worthwhile goal, although it takes a little more effort initially as it would ultimately allow for a quick substitution of the ViewModel, especially if you wished to test an application page layout and navigation without interaction with final application resources.
Consider:
private readonly IMyControlViewModel viewModel;
public MyControlView(IMyControlViewModel viewModel)
{
InitializeComponent();
if(viewModel==null) viewModel = new MockMyControlViewModel(); //example of optional code
this.DataContext = viewModel;
}
MockMyControlViewModel can be used for design time data context and at runtime until such time as a real implementation has been produced.
You could use a Dependency Injection container or utilize the Factory Pattern to code much of the functionality yourself.
For example a concrete instance of IMyControlViewModel can then be injected into the View by a NavigationService that controls the actual switching between application pages.

Can I dynamically size dialogs that are opened with IDialogService in Prism?

Is there any way to dynamically size a dialog in code in Prism using IDialogService? I would like to adjust the size of my dialog based on the user's screen resolution.
Here's how I'm opening my dialog:
public class MainViewModel
{
// Gets injected in the constructor
private IDialogService dialogService;
private void OpenDialog()
{
this.dialogService.ShowDialog(
nameof(MyDialog),
new DialogParameters(),
result => { });
}
}
Here's what my dialog looks like in XAML
<UserControl
x:Class="MyApplication.MyDialog"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<!-- various controls -->
</UserControl>
The easiest way is to expose Width and Height properties in your view model and bind to them. The drawback is that width and height are purely view related and should not not be accessible in a view model in pure MVVM.
I would like to adjust the size of my dialog based on the user's screen resolution.
If the size adjustment is related to the user's screen resolution, you should consider creating an attached behavior for either your custom dialog window or the dialog user control. This way you can encapsulate the logic for screen resolution adaption in a reusable component that resides in XAML and maintains separation of view and view model concerns. Furthermore, you will have access to the associated window or user control in the behavior, which makes it easier to handle even more compley scenarios without violating MVVM principles.

How to change Content property in ContentControl

I have custom ContentControl
public class MyContentControl: ContentControl
{....}
with Content defined in XAML like this
<controls:MyContentControl x:Name="myContentControl">
<controls:MyContentControl.Content>
<controls:UserControl1 />
</controls:MyContentControl.Content>
</controls:MyContentControl>
Content shows in designer and in the device when I launch my application. But when I try to change Content property programmatically, for example
UserControl2 control2 = new UserControl2();
myContentControl.Content = control2;
MyContentControl shows nothing. Using standard ContentControl give the same result.
Any suggestions are welcome.
I followed your code to make simple code sample to test. There's no problem.
public class CustomContentControl:ContentControl
{//......}
<Grid>
<local:CustomContentControl x:Name="content">
</local:CustomContentControl>
</Grid>
MyUserControl1 myUserControl1 = new MyUserControl1();
content.Content = myUserControl1;
<UserControl
x:Class="AppContent.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppContent"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBox Text="abc"></TextBox>
</Grid>
You might have done some specific settings in your code. #Martin Zikmund's suggestion also was reasonable. You could refer to his suggestion and check your code. After that, if you still could not solve this issue, please provide a Minimal, Complete, and Verifiable example.
This should work. The reason could be that the control does not stretch and is displayed just 0x0 in size. Try to set absolute Width and Height to the control2 and check if it displays. You can also set myContentControl.HorizontalContentStretch and myContentControl.VerticalContentStretch.
You can try running the app in debugger and then use the Live Property Explorer to see what the actual size of the control inside Content is.
Ok, I found out where the things went wrong. I am using different controls for desktop and mobile devices, so I put some of theirs XAML views to the DeviceFamily-Mobile folder. This way they automatically use when needed. I've confused namespaces, because all XAML views in this folder have a root namespace for accessibility reasons. When I was trying to add control to the ContentControl via c#, I didn't resolve namespace where my controls were placed. So I've put XAML view as a childs to the ContentControl, and they staying invisible as none of them has InitializeComponent() method. Adding correct controls with initialization fixed my problem.
I am very grateful for your answers, they pointed me to the right way.

WPF user control inheritance issues

I made a WPF control in a library project and would like to extend it with a new one.
<UserControl x:Class="Genesyslab.Desktop.Modules.ExtensionUtils85.GUI.EmbeddingUserControl"
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>
</UserControl>
I have tried to extend it like this:
<src:EmbeddingUserControl x:Class="Interaxalab.Desktop.Modules.PrototipoCable.CustomViews.InteractionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="Genesyslab.Desktop.Modules.ExtensionUtils85.GUI"
Name="InteractionWorksheetView" Height="321.613" Width="471.396"
>
<Grid>
<WindowsFormsHost x:Name="windowsFormsHost1" HorizontalAlignment="Left" Height="284" VerticalAlignment="Top" Width="471"/>
</Grid>
</src:EmbeddingUserControl>
However, I get an error message saying that the name "EmbeddingUserControl" does not exist in namespace "Genesyslab.Desktop.Modules.ExtensionUtils85.GUI".
The name clearly does exist, since the xaml.cs can find it, but for some reason the xaml cannot.
Any help would be appreciated.
Long story short - you cannot inherit control with xaml by another control with xaml (and does it makes sense even to do so?). In your case, EmbeddingUserControl does not contain any visual tree elements (just empty grid), so you can just do:
public class EmbeddingUserControl : UserControl {
// some methods, properties of your control
}
Then you can inherit exactly like you do already in your question (don't forget to inherit from EmbeddingUserControl both in xaml file and in code-behind file).
You can also inherit from user control with xaml, if your inherited control does not have xaml itself (so you can add\override logic).
If you need to inherit some visual stuctures - you have to switch from inheritance to composition. That is: your base control provides some placeholders where other controls may be placed, or even better allows to provide templates to format data items (like for example ItemsControl and ItemTemplate property). Then you just fill those placeholders with other controls if necessary.

Developing Custom Window into Class Library in WPF

I'm trying to develop Custom Window, which i can reuse in other applications.
I know that WPF cannot derive from XAML
I also tried to deploy it as Class Library, the code provided in this
video:
http://www.youtube.com/watch?v=EuhhL_NF-B0&feature=c4-overview&list=UUjwAVugYBMQemsMi9AD4SZA
, but still it does not read the XAML file.
I tried with code-behind to set the ControlTemplate, but as i read FrameworkElementFactory is deprecated...
All i want to do is, derive from Window, change the ControlTemplate, release it as Class Library... anyone can show me how or point me to the right direction?
In my opinion the best solution will be to create custom UserControl, and then load it from Window.xaml.
Once you created your user control just load it from Window like this:
<Window xmlns:my="clr-namespace:Styx.GUI.View" x:Class="Styx.GUI.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="800" Width="650" MinHeight="600" MinWidth="600">
<my:MainWindowUserControl />
</Window>

Categories

Resources