I am creating my main window inside the App.xaml.cs constructor like this:
MainWindow wnd = new MainWindow();
Application.Current.MainWindow = wnd;
wnd.Show();
Starting the application gives me a XamlParseException, the resource with the name "Locator" cannot be found.
This is the suspicious line:
<DockPanel x:Name="MainPanel" DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}" LastChildFill="True">
Using StartupUri in App.xaml works just fine.
What am I doing wrong?!
I presume you have your Locator resource in the App.xaml. The reason why it does not work when you put your code in the constructor is because App.xaml is not loaded yet. If you see the default Main method generated by visual studio, you can see that App.InitializeComponent is called after the constructor. The resources in the xaml file are initialized at this point.
You can fix the issue by putting your code in the Application.Startup event which Occurs when the Run method of the Application object is called. (If StartupUri is set, it's also initialized after Run is called.)
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var window = new MainWindow();
window.Show();
}
Of course you can subscribe to that event and write the code in an event hander. However, when we want to subscribe to events in a base class, it's better to override the OnXXX method for the corresponding event.
On and btw, you don't need this line Application.Current.MainWindow = wnd;. It will be done automatically for you by wpf.
Related
WPF WebView2 Control is inside the MainWindow.xaml (shown below). When calling ExecuteScriptAsync(...) from a Button click event inside MainWindow.xaml.cs (code shown below), it works fine. But when accessing WebView2 control from another class AnotherWindow.xaml.cs (in the same project) and call the same ExecuteScriptAsync(...) method it complains about CoreWebView2 being null
Question: What I may be missing, and how can it be resolved?
MainWindow.xaml:
<Window x:Class="WpfWebView2TEST.MainWindow"
.....
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
mc:Ignorable="d"
Style="{StaticResource CustomWindowStyle}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button x:Name="btnTest" Content=Test" Click="btnTest_Click"/>
<wv2:WebView2 Name="webView" />
</Grid>
</Window>
Remark 1: Following works fine when button and its click event is inside MainWindow.xaml.cs
private async void btnTest1_Click(object sender, RoutedEventArgs e)
{
await webView.CoreWebView2.ExecuteScriptAsync("window.print();");
}
The debug mode shows below that CoreWebView2 is not null (and hence the code works):
Remark 2: Following does NOT work when button and its click event is inside another window AnotherWindow.xaml.cs in the same project but accessing the WebView2 control of the MainWindow.xaml
private async void btnPrint_Click(object sender, RoutedEventArgs e)
{
MainWindow mainWindow = new MainWindow();
await mainWindow.webView.CoreWebView2.ExecuteScriptAsync("window.print();");
}
The debug mode inside the AnotherWindow.xaml.cs shows below that CoreWebView2 is not null (and hence throws the error: Object reference not set):
You need to initialize the CoreWebView2 as described in the WebView2 docs:
Upon creation, the control's CoreWebView2 property will be null. This
is because creating the CoreWebView2 is an expensive operation which
involves things like launching Edge browser processes. There are two
ways to cause the CoreWebView2 to be created: 1) Call the
EnsureCoreWebView2Async method. This is referred to as explicit
initialization. 2) Set the Source property (which could be done from
markup, for example).
From your second question, you cannot use the Source property to provide the HTML of the document. You need to call EnsureCoreWebView2Async, wait for it to complete, and then call NavigateToString on the CoreWebView2:
await webView.EnsureCoreWebView2Async(null);
webView.CoreWebView2.NavigateToString(htmlString);
How to determine the DataContext of a specific control at design time in a WPF application?
DataContext might be specifically set in XAML, inherited or set somewhere in code and sometimes it is hard to figure out at design time which class the bindings are referring to.
What I usually try to do to find the DataContext class is to search the binding name in VS. For example I see the binding is something like
ItemSource = {Binding Items}
...I will search the text "Items" in order to get to the class but VS sometimes fails to find the searched text (I have several projects in the solution).
I would like to add an approach to StepUpĀ“s listing:
The design instance:
Just like you can define a run time data context, you can add a data context that is specifically for the design time via:
<Usercontrol x:Class="MyUserControl"
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:viewModels="clr-namespace:MyProject.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MyViewModel}"
d:DesignHeight="300"
d:DesignWidth="600"
mc:Ignorable="d">
</UserControl>
In Visual Studio you can then use IntelliSense for bindable properties and if your view model has an uncomplicated or even parameterfree constructor, it will be created in the Designer and you can test trigger or converters without having to start your application.
DataContext of Control is ViewModel. So there are many ways to set ViewModel for DataContext of View, and if you find your ViewModel, but there is no Items property in ViewModel, then it means that you should add such property to get work binding.
Also, I recommend you to see Debug->Windows->Output window where you can see binding info. That is you can know binding errors.
To conclude, I would like to show ways of setting ViewModel to DataContext:
There are many approaches to set DataContext:
The first approach. In view:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
The second approach. You should override OnStartUp() method of App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
ProductViewModel context = new ProductViewModel();
app.DataContext = context;
app.Show();
}
}
The third approach. In constructor of Windows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext=new MainWindowViewModel();
}
}
The fourth approach. You can set DataContext through DependencyInjection by UnityContainer or another IoC container. But DependencyInjection, Prism and UnityContainer are other questions and goes from this scope of the question. Just for example:
protected override void RegisterTypes()
{
unityContainer.RegisterType<object, ItemControl>("ModuleAUpper");
unityContainer.RegisterType<IViewModelItemControl, ViewModelItemControl>();
unityContainer.RegisterTypeForNavigation<ItemControl>();
}
Issue
I want to load a referenced control from the main window into a new window. The referenced control is already child of the main window, causing the following exception when attempting to render the new window:
System.ArgumentException was unhandled:
Must disconnect specified child from current parent Visual before attaching to new parent Visual.
I do not want to disconnect it from the main window and I also cannot create a new instance of the control since I do not know how it's instantiated or what members are applied.
Background
I'm developing an application that allows developers to extend the application with additional views of configuration options. The container of these views may turn out to be too small for large view extensions (imagine a scheduling control for agendas as example), so I wish to provide the user with the ability to open the extended view in a new window.
Code
So far I've created a behavior to attach to Hyperlinks that opens a new window with the referenced control upon the Click event. The following code is the most basic implementation to demonstrate my intention:
public class ExpandViewBehavior : Behavior<Hyperlink>
{
public static DependencyProperty ViewProperty = DependencyProperty.Register("View", typeof(object), typeof(ExpandViewBehavior));
public object View
{
get { return GetValue(ViewProperty); }
set { SetValue(ViewProperty, value); }
}
protected override void OnAttached()
{
this.AssociatedObject.Click += AssociatedObject_Click;
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
if (View != null)
{
var window = new Window()
{
Content = View
};
window.Show();
}
}
}
Attached to a Hyperlink in the main window, referencing a simple TextBox to load in the new window. Where i is the System.Windows.Interactivity namespace and local my project namespace.
xmlns:local="clr-namespace:WpfApplication"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...
<StackPanel>
<TextBlock>
<Hyperlink>
<i:Interaction.Behaviors>
<local:ExpandViewBehavior
View="{Binding Source={x:Reference SomeControl}}" />
</i:Interaction.Behaviors>
<TextBlock
Text="(Open in new window)" />
</Hyperlink>
</TextBlock>
<TextBox
x:Name="SomeControl" />
</StackPanel>
My question is, is there is a way to load the referenced control without disconnecting it from the main window?
As you can't display the same control in two places at once, you will either need to disconnect the control from the main window (as noted in the error text), or create a copy of the control to place into the child window.
You can clone the control by exporting its XAML, and then create a new control from this XAML.
See this answer How can you clone a WPF object? for more details.
My app's main UI is in MainWindow.xaml.
Firstly - what is causing this to be the window the application opens on startup. It does not appear to be defined as a "starup object" and there does not appear to be any code that specifically launches this window.
I can make my login window appear when the app starts by in the loaded event of MainWindow.xaml defining a new "login.xaml" and telling it to show as a dialog. However, if I do this then MainWindow does not appear until Login has been closed.
What I want to achieve is when my app starts, the MainWindow appears and then on top of that the Login window is displayed modally.
How can this be done?
The startup of the MainWindow is defined in App.xaml by default when creating a project in VS:
<Application ...
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Creating the dialogue in the Loaded event should work, just don't do it in the constructor where it is not yet loaded.
Loaded="Window_Loaded"
private void Window_Loaded(object sender, RoutedEventArgs e)
{
new LoginDialogue().ShowDialog();
}
One way could be to add a Loaded event handler to your main window and in that display the login window:
this.Loaded += LoadedEventHander;
void LoadedEventHander(object sender, RoutedEventArgs e)
{
// Show Login.xaml here.
}
In app.xaml the following line defines the startup window, you can change it if you want
StartupUri="MainWindowView.xaml"
If you are following MVVM you can bind a command to the windows loaded event using System.Windows.Interactivity (otherwise simply create an event handler as the others have suggested)
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding MyICommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
I'm trying to use the search text box (which I made by following this tutorial: http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/).
I use MVVM & WPF. The above user control works when you write the "Search"-event in the code-behind file of the View, but I can't get it to work with a command (using the ViewModel).
(The search-event fires when you haven't typed something for about 2 seconds.)
I've tried using Caliburn, so it can "map" the view event to the viewmodel method. However when the event fires, the application crashes: "No target found for method SearchText()." on the RaiseSearchEvent method from the custom user control.
See the following test application: Test application
Could somebody tell me what I'm doing wrong? I told CaliBurn to do the following:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText()" />
</i:EventTrigger>
</i:Interaction.Triggers>
So I figure this is correct. It means that when the "Search" event fires, caliburn will look for the method SearchText in the ViewModel. This doesn't happen though, and it causes my app to crash and burn.
Do you know why? Or how I could solve this problem (doesn't have to be with Caliburn).
I already tried adding "Extending Command Support" (http://msdn.microsoft.com/en-us/library/dd458928.aspx), but this is a little too complex for me :/
Thanks for any help!!
You are using the Caliburn's ActionMessage but because you do not use its Bootstrapper class to start up your application, the MainView's DataContext is not set to an instance of the MainViewModel. If you check the SearchTextBox's DataContext at runtime, you'll see it's null.
Here's a series of steps that may solve your problem (using your linked example project)
Create a class called MyBootstrapper. It should look like this
public class MyBootstrapper : Bootstrapper<MainViewModel> {}
Add your new bootstrapper to the Application's Resources collection, like I show below (App.xaml)
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplicationParadise"
x:Class="WpfApplicationParadise.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:MyBootstrapper x:Key="bootstrapper" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Not sure why, but if the bootstrapper isn't nested in my build, it never is instantiated when App.InitializeComponent() is run...
Change App.xaml.cs to simply run InitializeComponent. Note that I had to tweak your build a bit to get this to work... InitializeComponent() is only defined in the App.g.cs file if you have the nested resource dictionary from step 2, or if you have an x:Name attribute on App.xaml, or perhaps other things...
using System.Windows;
namespace WpfApplicationParadise
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}
}
}
Finally, you need to remove the parens as Wallstreet Programmer suggested.
Those steps should cause your App to instantiate your bootstrapper, which in turn instantates the MainViewModel as the root viewmodel of your application, and then create a MainView and hook up its DataContext to the MainViewModel. At that point, your application should work as expected.
Remove ()
<cal:ActionMessage MethodName="SearchText" />
After I run your application, I see that you need to initialize the MainViewModel and also to bind Text of SearchTextBox with TekstBoxTekst.
Codebehind
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.DataContext = new MainViewModel();
};
}
}
XAML
<StackPanel>
<l:SearchTextBox
Text="{Binding TekstBoxTekst, UpdateSourceTrigger=PropertyChanged}"
Height="24" x:Name="TekstBoxTekst" Margin="145,144,145,143">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText">
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</l:SearchTextBox>
</StackPanel>