I'm sorry if this is a stupid question or doesn't even fall under what I'm asking, but I'm new to WPF and I can't seem to get the hang of it. Right now I'm doing something akin to https://www.c-sharpcorner.com/article/use-inotifypropertychanged-interface-in-wpf-mvvm/ and I've run into a problem. When I try to execute my code:
namespace DPS_Calculator_Prototype
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
}
}
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChange(string propertyName) {
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
}
public class Calculator: NotifyPropertyChanged
{
private string _damage;
public string Damage {
get { return _damage; }
set {
_damage = value;
RaisePropertyChange("Damage");
}
}
}
namespace UseOf_INotifyPropertyChanged
{
public class MainWindowViewModel : Calculator
{
public MainWindowViewModel() {
Damage = "7";
}
}
}
}
I get the error that "The type or namespace name 'ViewModel' does not exist in the namespace 'DPS_Calculator_Prototype.UseOf_INotifyPropertyChanged' (are you missing an assembly reference?)"
and
"The name "MainWindowViewModel" does not exist in the namespace 'clr-namespace:DPS_Calculator_Prototype.UseOf_INotifyPropertyChanged.ViewModel'."
and
"The type 'VM:MainWindowViewModel' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built."
I get the first error twice, once in MainWindow.g.cs and another in MainWindow.xaml. The other two are all in MainWindow.XAML If anyone can tell me what I'm doing wrong then that would be great. Here's the XAML file:
<Window x:Class="DPS_Calculator_Prototype.MainWindow"
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:VM="clr-namespace:DPS_Calculator_Prototype.UseOf_INotifyPropertyChanged.ViewModel"
xmlns:local="clr-namespace:DPS_Calculator_Prototype"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<VM:MainWindowViewModel x:Name="VMMainWindow">
</VM:MainWindowViewModel>
</Window.DataContext>
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap"
Text="{Binding Damage}" VerticalAlignment="Top" Width="120"
Margin="78,28,0,0" TextChanged="TextBox_TextChanged"/>
</Grid>
</Window>
That's one of the worst "copy and paste" jobs I've ever seen guy...
I don't know where to start.
Just to run the application you MUST:
change the namespace VM as follows;
xmlns:VM="clr-namespace:DPS_Calculator_Prototype.UseOf_INotifyPropertyChanged"
DPSCalculatorPrototype.ViewModel doesn't exist.
The TextBox_TextChanged doesn't exist inside the codebehind of the window. You must add the method
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
//Do your stuffs
}
in the MainWindow class.
In order to avoid headaches to you or who is reading your code, you
SHOULD
use one .cs file for each class.
Avoid to nest namespaces inside the same .cs file and create a folder tree that replicates the namespace structure. In your snippet just create a UseOf_INotifyPropertyChanged folder inthe root and create the MainWindowViewModel class inside that.
The purpose of a namespace must be clear reading the code. Create a DPS_Calculator_Prototype.ViewModels and put all application viewmodel inside it.
I just tried using different namespaces and keep them more simple. And it works.
DPSCalculatorPrototype.ViewModel
namespace DPSCalculatorPrototype.ViewModel
{
public class MainWindowViewModel : Calculator
{
public MainWindowViewModel()
{
Damage = "7";
}
}
}
DPSCalculatorPrototype
namespace DPSCalculatorPrototype
{
public class Calculator : NotifyPropertyChanged
{
private string _damage;
public string Damage
{
get { return _damage; }
set
{
_damage = value;
RaisePropertyChange("Damage");
}
}
}
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChange(string propertyName)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow.xaml
<Window x:Class="DPSCalculatorPrototype.MainWindow"
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:VM="clr-namespace:DPSCalculatorPrototype.ViewModel"
xmlns:local="clr-namespace:DPSCalculatorPrototype"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<VM:MainWindowViewModel x:Name="VMMainWindow"></VM:MainWindowViewModel>
</Window.DataContext>
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding Damage}" VerticalAlignment="Top" Width="120" Margin="78,28,0,0" TextChanged="TextBox_TextChanged" />
</Grid>
</Window>
MainWindow.xaml.cs
namespace DPSCalculatorPrototype
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
}
}
}
The reason you are seeing these errors is that WPF is looking in the namespaces mentioned and cannot seem to find what you're looking for. If you take a look at your XAML code you can see the line that says:
xmlns:VM="clr-namespace:DPS_Calculator_Prototype.UseOf_INotifyPropertyChanged.ViewModel"
This is what is declaring to use the namespace, so we need to point it to the correct area. Change your XAML to look like the following:
<Window x:Class="DPS_Calculator_Prototype.MainWindow"
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:VM="clr-namespace:DPS_Calculator_Prototype.UseOf_INotifyPropertyChanged"
xmlns:local="clr-namespace:DPS_Calculator_Prototype"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<VM:MainWindowViewModel x:Name="VMMainWindow">
</VM:MainWindowViewModel>
</Window.DataContext>
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="
{Binding Damage}" VerticalAlignment="Top" Width="120" Margin="78,28,0,0"
TextChanged="TextBox_TextChanged"/>
</Grid>
</Window>
You were getting these errors because you had "ViewModel" in the namespace declaration, and this namespace doesn't exist, and as such, nothing exists in it.
Related
I'm working on a WinUI app (c# and xaml) with multiple frames and pages.
The problem is that I need to modify a UIElement property (TextBox.Text) from another class. I've been trying so many things and none of them has worked yet.
I'd be glad if someone could inspire me with some useful ways to do it. It can be anything aside from xaml data binding (<property={"Binding bindingName"}).
Thanks for the help.
In this sample code, I have 2 pages (Page1 and Page2) and 1 ViewModel (MainViewModel). Each page has a TextBox that are bound to the same property in the MainViewModel.
If you change the text in Page1, the text in Page2 will be changed, and vice versa.
NuGet packages
CommunityToolkit.Mvvm
Microsoft.Extensions.DependencyInjection
App.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
namespace MultiplePagesSingleViewModel;
public partial class App : Application
{
private Window? window;
public App()
{
this.InitializeComponent();
Ioc.Default.ConfigureServices(
new ServiceCollection()
// This needs to be Singleton
.AddSingleton<MainViewModel>()
.BuildServiceProvider());
}
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
this.window = new MainWindow();
this.window.Activate();
}
}
Page1.xaml
<Page
x:Class="MultiplePagesSingleViewModel.Page1"
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:local="using:MultiplePagesSingleViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<StackPanel>
<TextBlock Text="Page 1" />
<TextBox Text="{x:Bind ViewModel.SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Page>
Page1.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
namespace MultiplePagesSingleViewModel;
public sealed partial class Page1 : Page
{
public Page1()
{
this.InitializeComponent();
// This MainViewModel is the same instance that Page2 gets.
ViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
}
public MainViewModel ViewModel { get; }
}
Page2.xaml
<Page
x:Class="MultiplePagesSingleViewModel.Page2"
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:local="using:MultiplePagesSingleViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<StackPanel>
<TextBlock Text="Page 2" />
<TextBox Text="{x:Bind ViewModel.SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Page>
Page2.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
namespace MultiplePagesSingleViewModel;
public sealed partial class Page2 : Page
{
public Page2()
{
InitializeComponent();
// This MainViewModel is the same instance that Page1 gets.
ViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
}
public MainViewModel ViewModel { get; }
}
MainViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace MultiplePagesSingleViewModel;
// This class needs to be "partial" for CommunityToolkit.Mvvm.
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
// The CommunityToolkit.Mvvm will automatically generate
// a UI-Interactive "SomeText" property for you.
private string someText = "Default text";
}
MainWindow.xaml
<Window
x:Class="MultiplePagesSingleViewModel.MainWindow"
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:local="using:MultiplePagesSingleViewModel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid ColumnDefinitions="*,*">
<local:Page1 Grid.Column="0" />
<local:Page2 Grid.Column="1" />
</Grid>
</Window>
Well, finally after two weeks of research I've found and check a solution.
The solution is basically rising and catching events from the different classes. I found this information in a MSDN post. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines
This post is REALLY STRAIGHT FORWARD. I've created the CustomEventArgs class and then the publisher class is my controller (.cs) and my subscriber is the model (.xaml.cs).
The only problem that I've encountered is setting alll the methods and attributes as static in order to be able to call the controller function from other models. The problem is that on the raiseEvent(this, e); in the publisher class, this cannot be static so i've written null instead.
Also, I'd like to say that it works extremely fluent and with no lag or delay at all even if I constantly use it.
I hope this helps to everybody encountering the same problem.
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.
I have been a few hours now trying to understand how to do data-binding.
Initially I was following some examples but they all show to do the databinding using {Binding Source={StaticResource myObject}, Path=myObject.myProperty}
or {Binding Path=myObject.myProperty}
Nothing of this seem to bind the Config object inside the controller that is inside the Window.
If I do the binding as an StaticResource it does the binding to an object of the Controller class but is NOT the object that is created inside the window class, this Config seems to be a new separate instance. This is the part I don't understand. If someone could explain or give me some reference where to look I would greatly appreciate it.
This is some code very simplified
Window1.cs
<Window x:Class="Sample.UI.Main"
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:controller="clr-namespace:Sample.Controller"
mc:Ignorable="d"
Title="SampleApp" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<controller:PublisherController x:Key="oController" />
</ResourceDictionary>
</Window.Resources>
<CheckBox x:Name="chkBoxShowRoom" Style="{StaticResource checkBoxTemplate}" Content="{StaticResource configShowRoom}" IsChecked="{Binding Source={StaticResource oController}, Path=Config.ShowRoom}"/>
Then my Window1.cs
public partial class Main : Window
{
public PublisherController Controller { get; set; }
Then Controller.cs
public class PublisherController
{
public Configuration Config { get; set; }
Then the Configuration.cs
public class Configuration : AbstractEntity, INotifyPropertyChanged
{
private bool _ShowRoom;
public bool ShowRoom
{
get
{
return _ShowRoom;
}
set
{
if (value != _ShowRoom)
{
this._ShowRoom = value;
OnPropertyChanged();
}
}
}
...
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.
I'm pulling my hair out. I have created a class "employee.cs". I developed this class originally within the "public partial class Window1 : Window" on "Window1.xaml.cs". When moving it to a sepate class I can no longer refernce textBoxes, comboBoxes etc. What do I do?? Error given is "The name 'textBox1' does not exist in the current context". I'm sure its simple! Thanks Guys!
Here's a cut back example!
Window1.xaml
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="100" Margin="12,12,23,0" Name="textBox1" VerticalAlignment="Top" />
</Grid>
</Window>
Window1.xaml.cs
namespace WpfApplication6
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
textBox1.Text = "testing"; //Works Here!
}
}
}
Class.cs
namespace WpfApplication6
{
class class1
{
public static void main()
{
textBox1.Text = "Help"; //Doesn't Work Here!! :-(
}
}
}
As the other answer here implies, you're going to need to change your class attribute in the Window XAML.
<Window x:Class="WpfApplication6.class1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="100" Margin="12,12,23,0"
Name="textBox1" VerticalAlignment="Top" />
</Grid>
</Window>
This change should make your textbox references work.
x:Class="WpfApplication6.Window1 in the xaml
Tells you this is part of the Window1 class. The window (from xaml) will be come a partial member of that class.