A bit of a complex one. I'm using WPF MVVM and I need to display results in a grid, then when you double click on one it loads that result in a separate popup window.
To do this, I have created a ResultViewerService which takes as parameters the ViewModel to use as content for this popup and a callback to call when the window is closed.
What I want to be able to do, is set a dependency property on the ViewModel and have this re-size the popup windows width based on the property's value. So if the user clicks a button, I can use the ViewModel to double the popup window's width and "expand" the window sideways to show all results. If the user then clicks contract, it returns to the original width.
I can re-size all the content within the window but because of the way I am creating the popup programmatically, I can't figure out how to link the Width of the popup window to the property on the ViewModel contained within the content.
My popup creation service method is as follows
public static Window OpenNew(ResultsViewModel viewModel, ResultsMainViewModel.CallbackResult callback)
{
var resultViewer = new ResultViewer(callback) { DataContext = viewModel };
var panel = new StackPanel { Margin = new Thickness(0), Name = "ParentPanel" };
panel.Children.Add(resultViewer);
var win = new Window
{
Owner = Application.Current.MainWindow,
WindowStyle = WindowStyle.ToolWindow,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Opacity = 1,
ShowInTaskbar = false,
ResizeMode = ResizeMode.NoResize,
Height = 690,
Width = ResultsConstants.ResultsWidthExpanded,
Margin = new Thickness(0),
Padding = new Thickness(0),
Content = panel,
Topmost = false,
Background =
new ImageBrush
{
ImageSource =
new BitmapImage(new Uri(#"pack://application:,,,/WealthMarginAnalyser;component/Resources/Background2.jpg", UriKind.Absolute))
}
};
win.Closed += resultViewer.ResultViewer_Closed;
win.Show();
return win;
}
The ResultViewer class is the XAML user control that I use for the popup content. So all I am doing in the popup service above is creating a Window programmatically, setting the content as a StackPanel with only one child, the ResultViewer user control which has the DataContext set to the ViewModel. I then set a number of properties on the window programmatically such as width, margin, show in taskbar etc and then show the popup window.
You can see in the above example I am currently hard coding the Width property of the window to the Expanded size, just for testing. But I want this Width property instead to take it's value from the ViewModel's dependency property I have created called ScreenWidth. When I update that property, I want the window to resize accordingly.
How can I achieve this?
I have tried in the past to create Bindings manually eg
Width = new Binding(...)
But I'm not sure if I can achieve this result this way or how I would do it.
The other way is from the ViewModel, to somehow get a reference to the popup window as an object (not pure MVVM of course) and set the Width manually.
Can anyone help me with suggestions?
UPDATE:
I have added the following line to the Window object creation in my service above
DataContext = viewModel
So the window itself has it's datacontext set to the VM, not just the ResultViewer user control. I then have added the following line after the window object creation.
win.SetBinding(FrameworkElement.WidthProperty, new Binding("ScreenWidth"));
This almost works. The window initially loads at the correct size when I open the popup and if I click expand, all the content resizes, but the window itself doesn't. If you close the popup however and re-open it immediately, it comes up at the expanded size. So it seems to be picking up the dependency property fine but ONLY on window creation. If you update the dependency property while the popup is open, it doesn't get the notification that something has changed and to change the width dynamically. I am calling RaisePropertyNotification as required and the user control updates in real-time, so property notification is working, it just doesn't work from the parent window width's perspective.
Is there something else I need to do to wire the INotifyPropertyChanged up to the windows binding I have created programmatically?
At first, you can create new Window using VisualStudio (e.g. Add->New Item->Window(WPF)).
After this you can set all properties of window in XAML:
Width="{Binding Path=MyWidth, Mode=TwoWay}" ShowInTaskbar = "False", ...
Then you can instantiate your window and give it data context:
....
var window = new MyWindow();
window.DataContext = MyViewModel;
win.Closed += resultViewer.ResultViewer_Closed;
win.Show();
return win;
...
Hope this help.
I suggest you do all this in XAML. Create a ResultsWindow, assign its DataContext to the view model and add whichever bindings you need. For example:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.ResultsWindow"
Height="{Binding WindowHeight}"
WindowStyle="ToolWindow"
Background="...">
<ResultViewer />
</Window>
Then in code:
var win = new ResultsWindow { DataContext = viewModel };
win.Show();
Related
I'm fiddling around with Microsofts WebView2 control. I created a simple wpf application, nothing fancy: Just MainWindow with a tabcontrol. Every tab has it's own user data directory.
Then I added a method to instantiate a tabitem and a webview and then add that control to the tabcontrol in MainWindow.
What I have so far - which is working (but...):
private async void AddTab(string serviceName, string serviceUrl)
{
TabItem tabItem = new TabItem();
tabItem.Name = serviceName;
tabItem.Header = serviceName;
myTabControl.Items.Add(tabItem);
// Create a unique user data directory for this tab
// and keep it "portable", a.k.a. in the programm directory
string userDataDirectory = System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + "\\Sessions\\" + serviceName;
// Create a new WebView2 control and pass custom user data directory
WebView2 webView = new WebView2() { CreationProperties = new CoreWebView2CreationProperties() { UserDataFolder = userDataDirectory } };
// set the source address
webView.Source = new Uri(serviceUrl);
// add webview to tab content
tabItem.Content = webView;
// Initialize the WebView2 control
await webView.EnsureCoreWebView2Async();
}
This works as intended. But as it is now, the webview content is only loaded when I click on the tab. I'm not sure how to make the tabs load in the background, so that when I click on the tab, the website is already loaded.
I found one example on stackoverflow, but it is too complex for what I want to accomplish.
My experiment with a Task brought me into the crazy world of not being able to access my thread again. Such code is way over my head (for now). I'm still learning.
I would try defining the tabitems in xaml.
The tabcontrol is an itemscontrol. I would usually bind the itemssource to a collection of viewmodels and template those out into ui. You only get whichever one of those items is selected instantiated if you bind the items like that. Defining tabitems in xaml seems to mean they're all instantiated. It's noticeably slower.
You can add other tabs afterwards in code if you wanted. But I'd check what happens with some defined in xaml first.
Another thing you could try is hidden webview2 controls in the main view. Navigate to these fixed url immediately the window renders.
Move them out the window and into your tabitem on selecting that tab, set isvisible true. Then you're just incurring rendering.
Btw
The webview2 is a wrapper around a chromium edge browser. It displays some odd behaviour like it can overflow parent containers.
I am using MVVM Light WPF 4.
I have a ContentPresenter in my Home.xaml.
<ContentPresenter Name="MDI" Content="{Binding WindowName, Mode=OneWay}">
I am binding user control to this in viewmodel like
public UserControl WindowName { get; set; }
void ShowSalesEntry()
{
WindowName = null;
WindowName = new SalesEntry();
RaisePropertyChanged("WindowName");
}
by using command in a menu click and it is binding fine.
Now in the user control i have a button which i used to close (but to close i change the visibility
to collapsed) by this way..
Visibility="{Binding visibility, Mode=OneWay}"
in the user control view model,
public SalesEntryViewModel()
{
visibility = Visibility.Visible;
cmdExitWindow = new RelayCommand(ExitWindow);
RaisePropertyChanged("visibility");
}
and the following to close (visibility to collapsed)
public RelayCommand cmdExitWindow { get; set; }
void ExitWindow()
{
visibility = Visibility.Hidden;
RaisePropertyChanged("visibility");
}
To exit (means visibility collapsed)..
This is working fine upto this.
Problem is when i click the same page i mean to show the same user control,
now this time the visibility is still collapsed. Even though i changed to visible in the
load event.
How to solve this..
I am new to MVVM WPF.. Please help me..
Problem is when i click the same page i mean to show the same user
control, now this time the visibility is still collapsed. Even though
i changed to visible in the load event.
Based on this comment and the code provided, you've either omitted code, or you've confused the purpose of the constructor.
In your constructor, you have set the Visibility to Visible. You then have a method that sets the Visibility to Hidden, but there is nothing to ever set it back to Visible once this has occurred. The constructor only fires when the object is created. You need something to set the Visibility back at the appropriate time (ie. your comment "when i click the same page").
//Add these lines to the method/event that will show the control again
visibility = Visibility.Visible;
RaisePropertyChanged("visibility");
That's the best answer I can give based on what you've provided.
To preface this question, I am working on coding the back end of an application whose UI was put together by someone else (I believe using Blend). The application consists of a series of "Screens," whose root element in XAML is "UserControl". There is no use of the "Window" tag anywhere in the source.
What I want to do is remove the Windows border that is added to the outside edge of the application when I run the program. The border currently consists of forward/backward buttons like a web browser, and an X button to close.
All I can find from searches are instructions to add
WindowStyle="None"
to the
<Window>
element. But of course, I don't have one of those, and WindowStyle is not a property of UserControl. Anyone know how to accomplish this with UserControl root elements?
Edit: The StartupUri for the application is
this.StartupUri = new Uri(#"pack://application:,,,/WpfPrototype1.Screens;Component/Screen_1.xaml");
the file it points to does not have a Window tag.
Based on the comments above it seems your MainWindow is created dynamically somewhere, however you can use the Application class to get the applications MainWindow.
var mainWindow = Application.Current.MainWindow;
And you can then set your border style from there
Example:
private void RemoveBorder()
{
var mainWindow = Application.Current.MainWindow;
if (mainWindow != null)//should never be
{
mainWindow.WindowStyle = System.Windows.WindowStyle.None; // removes top bar (icon, title, close buttons etc)
mainWindow.AllowsTransparency = true; //removes the border around the outside
}
}
When I click on a Button on my navigation bar, a new Window opens. For example:
private void btFinanzen_Click(object sender, RoutedEventArgs e)
{
Finanz mw = new Finanz(login);
mw.Show();
}
Now, I don´t want to show it in a new window. Instead of that, I want it to be shown at the same window. I tried with the following:
private void btFinanzen_Click(object sender, RoutedEventArgs e)
{
Finanz mw = new Finanz(login);
//mw.Show();
this.Content = Finanz f;
}
but it failed.
What I should say is, that I´ve many of different windows now and want them all to be shown at one window - each by clicking a different button.
How can I manage this?
What you have to do is to use a UserControl rather than a Window for your replaceable content. Add a new UserControl for each possible content you want to show in this.Content to your project. Add the content you had previously in the second window to the appropriate UserControl. After that you can simply instantiate the new control in your code and assign it to your content area of your main Window.
For example you create a UserControl ctlFinanz with the content of your previous Finanz window. Now you simply write:
this.Content = new ctlFinanz(login);
That's all :-)
I had the same problem and decided to create a seperate usercontrol for every window, put them in my wpf and set the visibility of all windows except the active one to Collapsed.
To me, this sounds like place to use a frame with pages. Instead of making lots of windows, make one window with a frame, then put the content into pages. Then all you have to do is change the frame's .Content to a different page each time you press a navigation button.
Create a main content with a UserControl that I set into the window constructor. And then I change the content with other UserControl depending on your needs.
MainWindow constructor:
this.Content = new UserControlMainContent();
From UserControlMainContent, when I click on a button by example:
Window window = Window.GetWindow(this); // Get the main window
window.Content = new OtherUserControlContent();
I have asked a question before about creating a child window here ... Now when I open child window, it doesn`t open centered to the parent window. How can I set it to open centered to the parent window?
This solution worked fine for me.
Here’s a method I’ve found for centering a window to either its parent or the main window for the application, in WPF. It’s not too different from how you do it in WinForms.
For the child window, set its WindowStartupLocation to “CenterOwner”. This will cause it to show in the center of the owning Window.
Collapse
<Window x:Class="WpfApplication1.TestChild"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestChild" Height="300" Width="300"
WindowStartupLocation="CenterOwner">
Now, all that’s left to do is set its owner before displaying it. If the code you’re using to display the window is running inside of a Window class, then you can just use this.
Collapse
TestChild testWindow = new TestChild();
testWindow.Owner = this;
testWindow.Show();
This isn’t always the case, however; sometimes, you need to display the child window from the code running on a page or a user control. In this case, you want the child window to be centered to the main window of the application.
Collapse
TestChild testWindow = new TestChild();
testWindow.Owner = Application.Current.MainWindow;
testWindow.Show();
Try this.
aboutWindow.WindowStartupLocation= WindowStartupLocation.CenterOwner ;
aboutWindow.ShowDialog(this);
You can try this:
AboutWindow window = new AboutWindow();
window.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
window.Owner = this;
window.ShowDialog();