Opening second window from main window in WPF/MVVM app - c#

Could you tell me how to in pure MVVM way call (I mean open/show) child window from parent window. Let's say I have two Views:
MainWindow.cs (MainWindow.xaml) - parent window (DataContext = new MainWindowViewModel())
Window.cs (Window.xaml) - child window (DataContext = new WindowViewModel())
And corresponding ViewModel classes:
MainWindowViewModel.cs
WindowViewModel.cs
I would like my window to be opened after button click (button that is on the MainWindow view). Because of that I have defined command binding in MainWindow.xaml:
<Button x:Name="buttonOpenWindow" Content="Open window..." Width="100" Height="20" Command="{Binding OpenWindowCmd}"/>
And MainWindowViewModel.cs piece:
public ICommand OpenWindowCmd { get; set; }
public MainWindowViewModel()
{
OpenWindowCmd = new RelayCommand(o => OpenWindow());
}
private void OpenWindow()
{
// What to put here?
}
In Window.xaml I added something like that:
<Window x:Class="Namespace.View.Window"
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"
mc:Ignorable="d"
xmlns:vm="clr-namespace:Namespace.ViewModel"
Title="Title" Height="300" Width="325" Visibility="{Binding IsWindowVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
(...)
And the WindowViewModel.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Namespace.Annotations;
namespace Namespace.ViewModel
{
public class WindowViewModel : INotifyPropertyChanged
{
private bool _isWindowVisible;
public bool IsWindowVisible
{
get { return _isWindowVisible; }
set
{
_isWindowVisible = value;
OnPropertyChanged(nameof(IsWindowVisible));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am not sure what to do next and if that approach is correct. I found some services implementations in the forum, but I thought of using just Visibility property instead (but not sure if it is possible). I need to somehow change the IsWindowVisible in one of the view models I suppose. Could anyone suggest how to gently handle such sub window opening?

If I understood well, you need something like this:
private void OpenWindow()
{
WindowViewModel wvm = new WindowViewModel();
Window win = new Window()
{
DataContext = wvm;
};
win.Show();
}
If you don't like this solution then try the one from the comments with IWindowService.
In any case it makes no sense to use a Visibility property.

Related

How can I write a method that returns the value of a of xaml toggle switch?

I have two xaml toggles in separate files that I want to update simultaneously (if one is switched on the other should be too (and vice versa). My first switch in xaml is:
<Switch Grid.Column="1" x:Name="toggleSwitch1" IsToggled="true" Toggled="OnToggled"/>
Using C# how can I return a boolean value of this switch so that I can update another switch simultaneously? Then once retrieving the value, how can I update the xaml of the toggle status for the other switch?
Your Switch control means, as I can understand, that you using UWP, but I'm not sure.
Anyway, the idea is to bind both controls IsToggled properties to same property of some ViewModel:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MyWPFApp
{
public class ControlsViewModel : INotifyPropertyChanged
{
private bool switchToggled;
public bool SwitchToggled
{
get => switchToggled;
set
{
switchToggled = value;
OnPropertyChanged(nameof(SwitchToggled));
}
}
public ControlsViewModel() { }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in XAML of both Windows set bindings to Switch control (in my example - CheckBox control):
<!-- Window 1 -->
<Window x:Class="MyWPFApp.Window1"
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:local="clr-namespace:MyWPFApp"
mc:Ignorable="d"
Title="Window 1" Height="100" Width="300">
<Grid>
<CheckBox Content="Window1 CheckBox"
IsChecked="{Binding SwitchToggled}"/>
<!-- Replace IsChecked to IsToggled property -->
</Grid>
</Window>
<!-- Window 2 -->
<Window x:Class="MyWPFApp.Window2"
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:local="clr-namespace:MyWPFApp"
mc:Ignorable="d"
Title="Window 2" Height="100" Width="300">
<Grid>
<CheckBox Content="Window2 CheckBox"
IsChecked="{Binding SwitchToggled}"/>
<!-- Replace IsChecked to IsToggled property -->
</Grid>
</Window>
Code-behind of both Windows in example is same:
using System.Windows;
namespace MyWPFApp
{
public partial class Window1 : Window // or public partial class Window2
{
public Window1(ControlsViewModel cvm) // or public Window2
{
InitializeComponent();
DataContext = cvm;
}
}
}
And when calling that example Windows to show from Main one, you creating ControlsViewModel instance and pass it to both:
using System.Windows;
namespace MyWPFApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var cvm = new ControlsViewModel();
new Window1(cvm).Show();
new Window2(cvm).Show();
}
}
}
So checking/unchecking (toggle/untoggle) one of them will affect another and vice versa. Also, you can change SwitchToggled from code somewhere, which would affect both controls too.
Please note, that this is just example to try explain the idea. More MVVM pattern explanations and examples you can find at MSDN.

progressbar not updating binding

I am trying to make a progress bar that updates when a property value changes
I have followed other questions but i don't know what is wrong with it.
This is XAML code:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow">
<Grid Margin="0,0,-8,1">
<ProgressBar Value="{Binding Progreso, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" Margin="105,95,207,350"/>
<Button Content="Button" Click="Button_Click" Margin="218,232,333,217"/>
</Grid>
</Window>
it is basically a progress bar with the binding and a button with a listener that increases Progreso by 10
this is the C# code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string sProp)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sProp));
}
}
float progreso = 10;
public float Progreso
{
get
{
return progreso;
}
set
{
progreso = value;
NotifyPropertyChanged("Progreso");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Progreso = this.Progreso + 10;
}
}
I tried to keep it simple but I couldn't get it to work, any help with this would be appreciated.
edit: I've also tried UpdateSourceTrigger=PropertyChanged and that didn't work either
Your problem is you missed the declaration of INotifyPropertyChanged interface implementation like this:
public partial class MainWindow : Window, INotifyPropertyChanged {
//....
}
NOTE: Using RelativeSource works OK, I tested this. Using DataContext is just an implicit way to set the Source although it's a convenient and a recommended way.
UPDATE
About using DataContext:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
<ProgressBar Value="{Binding Progreso}" Margin="105,95,207,350"/>
AncestorType does not seem to work property. So you have two options:
Set Window Name and find DataContext by ElementName
Set DataContext to be this in code behind and remove RelativeSource part

Binding UserControl to its own dependencyProperty doesn't work

Im having a problem where I can't create a User Control which uses properties of an custom object when the parent has set that object to data bind.
To try an explain what I mean here is the code.
Custom object:
public class MyObj
{
public string Text { get; set; }
public MyObj(string text)
{
Text = text;
}
}
User Control Code Behind:
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public static readonly DependencyProperty ObjectProperty =
DependencyProperty.Register("Object", typeof (MyObj), typeof (MyControl), new PropertyMetadata(default(MyObj)));
public MyObj Object
{
get { return (MyObj) GetValue(ObjectProperty); }
set { SetValue(ObjectProperty, value); }
}
public MyControl()
{
InitializeComponent();
}
}
User control XAML:
<UserControl x:Class="Test.MyControl"
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" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding Object.Text}"/>
So all I expect is for MyControl to display a TextBlock with text showing whatever string is in MyObj.Text;
If I add the control in code, without any bindings, then this works Okay e.g.
MyControl myControl = new MyControl(){ Object = new MyObj("Hello World!") };
grid.Children.Add(myControl);
However if I try to use data binding this doesn't display anything, here is the code for MainWindow.
CodeBehind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private MyObj _Object;
public MyObj Object
{
get { return _Object; }
set
{
_Object = value;
OnPropertyChanged("Object");
}
}
public MainWindow()
{
InitializeComponent();
Object = new MyObj("HELLO");
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
Could anyone point me in the right direction, I guess it's something to do with using relative source binding on the UserControl but I'm not sure.
Thanks
I've personally never used a relative self binding on a UserControl, so I'm unsure if it works. You may try setting the x:Name of your UserControl, and use that in the binding.
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{Binding ElementName=window, Path=Object.Text}"/>
</UserControl>
Note that if a data-binding fails to bind at runtime, you should also see a related error message in the Output window.
it's been a long time .. but since there is a new technique i would like to post it here.
Compiled Time Binding : this is a new type of binding introduced with windows 10. this binding has a lot of performance benefits classic binding.
And the extra benefit you need not set any DataContext the Page or Control itself is the DataContext you can bind to anything in the page or Control
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{x:Bind Object.Text}"/>
</UserControl>
But does this work perfectly as you have imagined .. No!! not as u guessed.
and there is an answer to it .
Compiled time binding are by default set to OneTime as opposed to classic bindings that are se to OneWay.
so you need to explicitly set the mode to OneWay to ensure the value always updates.
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{x:Bind Object.Text,Mode=OneWay}"/>
</UserControl>

How can I bind two controls in two different windows on wpf?

I want to bind two controls like font size slider and text box, and each control is on different window on wpf, so how can I bind them?
Here's an example of how to do it:
1) Create a WPF project.
2) Change the contents of the MainWindow.xaml to the following (don't forget to correct the namespaces in all the code that I'm posting, for example in my code the namespace is WpfApplication2):
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Content="Settings Window" Click="SettingsWindowButton_OnClick"/>
<Button Content="Bound Window" Click="BoundWindowButton_OnClick"/>
</StackPanel>
</Window>
3) Change the contents of the MainWindow.xaml.cs to the following:
namespace WpfApplication2
{
using System.Windows;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
}
private void SettingsWindowButton_OnClick(object sender, RoutedEventArgs e)
{
var settingsWindow = new SettingsWindow();
settingsWindow.DataContext = viewModel;
settingsWindow.Show();
}
private void BoundWindowButton_OnClick(object sender, RoutedEventArgs e)
{
var boundWindow = new BoundWindow();
boundWindow.DataContext = viewModel;
boundWindow.Show();
}
}
}
4) Create a class named ViewModel in your project with the following code:
namespace WpfApplication2
{
using System.ComponentModel;
public class ViewModel : INotifyPropertyChanged
{
private int _fontSizeSetting = 10;
public event PropertyChangedEventHandler PropertyChanged;
public int FontSizeSetting
{
get { return _fontSizeSetting; }
set
{
_fontSizeSetting = value;
OnPropertyChanged("FontSizeSetting");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
5) Add two new Windows to your project named BoundWindow and SettingsWindow with the following markup:
<Window x:Class="WpfApplication2.BoundWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BoundWindow" Height="300" Width="300">
<Grid>
<TextBox FontSize="{Binding FontSizeSetting, Mode=TwoWay}" Text="test..."/>
</Grid>
</Window>
-
<Window x:Class="WpfApplication2.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SettingsWindow" Height="300" Width="300">
<Grid>
<Slider Value="{Binding FontSizeSetting, Mode=TwoWay}" Minimum="10" Maximum="100"/>
</Grid>
</Window>
Now everything should be working as expected. What you basically did was to create a view model to set as the DataContext of your Windows. They both bind to the FontSizeSetting property of your view model, and when you change it in one window, WPF binding system takes care of changing the other value automatically.

Making AvalonEdit MVVM compatible

I'm trying to make Avalon MVVM compatible in my WPF application. From googling, I found out that AvalonEdit is not MVVM friendly and I need to export the state of AvalonEdit by making a class derived from TextEditor then adding the necessary dependency properties. I'm afraid that I'm quite lost in Herr Grunwald's answer here:
If you really need to export the state of the editor using MVVM, then I suggest you create a class deriving from TextEditor which adds the necessary dependency properties and synchronizes them with the actual properties in AvalonEdit.
Does anyone have an example or have good suggestions on how to achieve this?
Herr Grunwald is talking about wrapping the TextEditor properties with dependency properties, so that you can bind to them. The basic idea is like this (using the CaretOffset property for example):
Modified TextEditor class
public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
{
public static DependencyProperty CaretOffsetProperty =
DependencyProperty.Register("CaretOffset", typeof(int), typeof(MvvmTextEditor),
// binding changed callback: set value of underlying property
new PropertyMetadata((obj, args) =>
{
MvvmTextEditor target = (MvvmTextEditor)obj;
target.CaretOffset = (int)args.NewValue;
})
);
public new string Text
{
get { return base.Text; }
set { base.Text = value; }
}
public new int CaretOffset
{
get { return base.CaretOffset; }
set { base.CaretOffset = value; }
}
public int Length { get { return base.Text.Length; } }
protected override void OnTextChanged(EventArgs e)
{
RaisePropertyChanged("Length");
base.OnTextChanged(e);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Now that the CaretOffset has been wrapped in a DependencyProperty, you can bind it to a property, say Offset in your View Model. For illustration, bind a Slider control's value to the same View Model property Offset, and see that when you move the Slider, the Avalon editor's cursor position gets updated:
Test XAML
<Window x:Class="AvalonDemo.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:avalonExt="clr-namespace:WpfTest.AvalonExt"
DataContext="{Binding RelativeSource={RelativeSource Self},Path=ViewModel}">
<StackPanel>
<avalonExt:MvvmTextEditor Text="Hello World" CaretOffset="{Binding Offset}" x:Name="editor" />
<Slider Minimum="0" Maximum="{Binding ElementName=editor,Path=Length,Mode=OneWay}"
Value="{Binding Offset}" />
<TextBlock Text="{Binding Path=Offset,StringFormat='Caret Position is {0}'}" />
<TextBlock Text="{Binding Path=Length,ElementName=editor,StringFormat='Length is {0}'}" />
</StackPanel>
</Window>
Test Code-behind
namespace AvalonDemo
{
public partial class TestWindow : Window
{
public AvalonTestModel ViewModel { get; set; }
public TestWindow()
{
ViewModel = new AvalonTestModel();
InitializeComponent();
}
}
}
Test View Model
public class AvalonTestModel : INotifyPropertyChanged
{
private int _offset;
public int Offset
{
get { return _offset; }
set
{
_offset = value;
RaisePropertyChanged("Offset");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You can use the Document property from the editor and bind it to a property of your ViewModel.
Here is the code for the view :
<Window x:Class="AvalonEditIntegration.UI.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:AvalonEdit="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit"
Title="Window1"
WindowStartupLocation="CenterScreen"
Width="500"
Height="500">
<DockPanel>
<Button Content="Show code"
Command="{Binding ShowCode}"
Height="50"
DockPanel.Dock="Bottom" />
<AvalonEdit:TextEditor ShowLineNumbers="True"
Document="{Binding Path=Document}"
FontFamily="Consolas"
FontSize="10pt" />
</DockPanel>
</Window>
And the code for the ViewModel :
namespace AvalonEditIntegration.UI
{
using System.Windows;
using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document;
public class ViewModel
{
public ViewModel()
{
ShowCode = new DelegatingCommand(Show);
Document = new TextDocument();
}
public ICommand ShowCode { get; private set; }
public TextDocument Document { get; set; }
private void Show()
{
MessageBox.Show(Document.Text);
}
}
}
source : blog nawrem.reverse
Not sure if this fits your needs, but I found a way to access all the "important" components of the TextEditor on a ViewModel while having it displayed on a View, still exploring the possibilities though.
What I did was instead of instantiating the TextEditor on the View and then binding the many properties that I will need, I created a Content Control and bound its content to a TextEditor instance that I create in the ViewModel.
View:
<ContentControl Content="{Binding AvalonEditor}" />
ViewModel:
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
// ...
private TextEditor m_AvalonEditor = new TextEditor();
public TextEditor AvalonEditor => m_AvalonEditor;
Test code in the ViewModel (works!)
// tests with the main component
m_AvalonEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML");
m_AvalonEditor.ShowLineNumbers = true;
m_AvalonEditor.Load(#"C:\testfile.xml");
// test with Options
m_AvalonEditor.Options.HighlightCurrentLine = true;
// test with Text Area
m_AvalonEditor.TextArea.Opacity = 0.5;
// test with Document
m_AvalonEditor.Document.Text += "bla";
At the moment I am still deciding exactly what I need my application to configure/do with the textEditor but from these tests it seems I can change any property from it while keeping a MVVM approach.

Categories

Resources