I'm pretty new to C# and WPF and I'm having a bit of trouble accessing a class that was instantiated during the MainWindow() constructor.
Here's my code:
namespace PivotBlockPicker.View
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
WrenchViewModel wrench = new WrenchViewModel();
MeasurementViewModel measurements = new MeasurementViewModel();
}
private void modelSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
wrench.GetWrench(modelSelector.SelectedItem);
}
}
}
And here's my XAML:
<Window x:Class="PivotBlockPicker.View.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:local="clr-namespace:PivotBlockPicker.View"
xmlns:vm="clr-namespace:PivotBlockPicker.ViewModel"
xmlns:m="clr-namespace:PivotBlockPicker.Model"
mc:Ignorable="d"
Title="MainWindow" Height="347" Width="288.666"
DataContext="vm:WrenchViewModel">
<Grid>
<GroupBox x:Name="wrenchBox" Header="Torque Wrench" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="262" Width="250">
<Grid>
<Grid Margin="0,0,0,140.667" Height="88" VerticalAlignment="Bottom">
<TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="10,3,0,0" TextWrapping="Wrap" Text="Model:" VerticalAlignment="Top"/>
<ComboBox x:Name="modelSelector" HorizontalAlignment="Left" Margin="52,0,0,0" VerticalAlignment="Top" Width="120" SelectionChanged="modelSelector_SelectionChanged">
<ComboBoxItem>QD2R200</ComboBoxItem>
<ComboBoxItem>QD2R50</ComboBoxItem>
</ComboBox>
<Grid HorizontalAlignment="Left" Width="228" Margin="0,27,0,0">
<TextBlock x:Name="blockFirstTP" Text="TextBlock" HorizontalAlignment="Left" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
<TextBlock x:Name="blockSecondTP" Text="TextBlock" HorizontalAlignment="Left" Margin="10,21,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
<TextBlock x:Name="blockThirdTP" Text="TextBlock" HorizontalAlignment="Left" Margin="10,42,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
</Grid>
</Grid>
<GroupBox x:Name="blockBox" Header="Pivot Block Dimensions" HorizontalAlignment="Left" Margin="10,100,0,0" VerticalAlignment="Top" Height="86">
<Grid>
<TextBlock x:Name="blockTextHorizontal" HorizontalAlignment="Left" Margin="51,12,0,0" TextWrapping="Wrap" Text="Vertical" VerticalAlignment="Top" Height="16" Width="40"/>
<TextBox x:Name="blockHorizontal" Text="" HorizontalAlignment="Left" Height="20" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="36" MaxLength="5"/>
<TextBlock x:Name="blockTextVertical" HorizontalAlignment="Left" Margin="51,37,0,0" TextWrapping="Wrap" Text="Horizontal" VerticalAlignment="Top" Height="16" Width="58"/>
<TextBox x:Name="blockVertical" Text="" HorizontalAlignment="Left" Height="20" Margin="10,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="36" MaxLength="5"/>
</Grid>
</GroupBox>
</Grid>
</GroupBox>
<Button x:Name="button" Content="Get Wrench Info" HorizontalAlignment="Left" Margin="185,277,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
My window has a ComboBox that, when the selection is changed, I want to call a function from the WrenchViewModel class that will populate some variables (that I will later databind to the TextBlocks.
However, my call to wrench.GetWrench results in a "The name wrench does not exist in the current context" build error.
Floating my mouse cursor over the the instance of "wrench" in the MainWindow constructor shows that it's a local variable.
How can I get this to be accessible to at least the MainWindow class?
You can move the variable declaration one scope up into the class, so it's available not only inside the constructor (local scope) but in the whole class:
public partial class MainWindow : Window
{
private WrenchViewModel wrench;
private MeasurementViewModel measurements;
public MainWindow()
{
InitializeComponent();
wrench = new WrenchViewModel();
measurements = new MeasurementViewModel();
}
private void modelSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
wrench.GetWrench(modelSelector.SelectedItem);
}
}
The curly brackets brackets in C# denote scope, the tell you where a method, class, namespace, or other scopes end. You cannot access anything outside it's own scope, or from another scope.
What you need to do is move the declaration of your variable outside the scope of the constructor. You still assign your view model in side the constructor, but the declaration is in a scope that the other method can see.
Like so:
public partial class MainWindow : Window
{
WrenchViewModel wrench;
public MainWindow()
{
InitializeComponent();
wrench = new WrenchViewModel();
Because you have defined a local variable:
public MainWindow()
{
InitializeComponent();
WrenchViewModel wrench = new WrenchViewModel();
MeasurementViewModel measurements = new MeasurementViewModel();
}
Inside of constructor.
You should change it to this private member:
public partial class MainWindow : Window
{
private WrenchViewModel _wrench = new WrenchViewModel();
private MeasurementViewModel _measurements = new MeasurementViewModel();
public MainWindow()
{
InitializeComponent();
}
private void modelSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_wrench.GetWrench(modelSelector.SelectedItem);
}
}
Related
I need some help. I created a custom User Control, and inserted it into the Main Window. However, Im not able to bind a Property in the Window to a DependencyProperty in the User Control.
Here's the User Control code.
XAML:
<UserControl x:Class="SomeExample.UCFullName"
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:SomeExample"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Margin="0,0,0,0" Orientation="Vertical" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel Orientation="Vertical">
<Label BorderBrush="White" BorderThickness="1" Content="First Name :" FontSize="14" FontWeight="SemiBold" Foreground="White" Height="30" HorizontalAlignment="Left" Margin="0,2,0,0" VerticalAlignment="Top" Width="100"/>
</StackPanel>
<Grid>
<TextBox Name="FirstName" BorderBrush="Black" BorderThickness="1" FontSize="14" FontWeight="SemiBold" Height="30" HorizontalAlignment="Left" Margin="2,2,0,0" MaxLength="20" VerticalAlignment="Top" Width="100" TextChanged="TxtBlock_TextChanged"/>
</Grid>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel Orientation="Vertical">
<Label BorderBrush="White" BorderThickness="1" Content="Last Name :" FontSize="14" FontWeight="SemiBold" Foreground="White" Height="30" HorizontalAlignment="Left" Margin="0,2,0,0" VerticalAlignment="Top" Width="100"/>
</StackPanel>
<Grid>
<TextBox Name="LastName" BorderBrush="Black" BorderThickness="1" FontSize="14" FontWeight="SemiBold" Height="30" HorizontalAlignment="Left" Margin="2,2,0,0" MaxLength="20" VerticalAlignment="Top" Width="100" TextChanged="TxtBlock_TextChanged"/>
</Grid>
</StackPanel>
</StackPanel>
And here's the code behind
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SomeExample
{
public partial class UCFullName : UserControl, INotifyPropertyChanged
{
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged implementation
public string ValueFullName
{
get { return (string)GetValue(ValueFullNameProperty); }
set
{
SetValue(ValueFullNameProperty, value);
Notify("ValueFullName");
}
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueFullNameProperty =
DependencyProperty.Register("ValueFullName", typeof(string), typeof(UCFullName), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public UCFullName()
{
InitializeComponent();
}
private void TxtBlock_TextChanged(object sender, TextChangedEventArgs e)
{
ValueFullName = FirstName.Text + " " + LastName.Text;
}
}
}
This is how it looks:
And here's the code of the Main Window:
<Window x:Class="SomeExample.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:local="clr-namespace:SomeExample"
mc:Ignorable="d"
Title="Some Example" Height="200" Width="400">
<StackPanel Margin="0,0,0,0" Orientation="Vertical" Name="SpManual" Background="Black">
<GroupBox Header="Name" Foreground="White" FontSize="14" Name="groupBoxCoordinateStart" >
<local:UCFullName ValueFullName="{Binding Path = PropertyFullName, Mode = TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"></local:UCFullName>
</GroupBox>
<StackPanel Name="SpBtnInsert" Orientation="Horizontal" HorizontalAlignment="Center" Visibility="Visible">
<Button Name="btnShowFullName" BorderBrush="White" BorderThickness="1" FontSize="14" FontWeight="SemiBold" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2,2,0,0" Background="Transparent" Content="Show Name" Foreground="White" Width="98" Click="BtnShowFullName_Click"></Button>
</StackPanel>
</StackPanel>
And the code behind:
using System.Windows;
namespace SomeExample
{
/// <summary>
/// Lógica de interacción para MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public string PropertyFullName { get; set; }
public MainWindow()
{
InitializeComponent();
}
private void BtnShowFullName_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Current full name :" + PropertyFullName);
}
}
}
And of course, I expected that when I pressed the button, I got a message with the full name entered by the user. However, I got nothing.
Edit: Here's the solution to the problem, for people who visit this page with a similar problem.
<local:UCFullName ValueFullName="{Binding Path = PropertyFullName, RelativeSource={RelativeSource AncestorType=Window}}"></local:UCFullName>
You are binding to the wrong AncestorType. Instead of UserControl the type must be Window. Window extends Control but not UserControl.
<local:UCFullName ValueFullName="{Binding Path=PropertyFullName, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=Window}}" />
Also because you set the Binding.Mode to TwoWay, the binding source PropertyFullName must be able to notify the binding target ValueFullName about value changes. To achieve this, you need to implement PropertyFullName as a DependencyProperty to enable two way binding.
As a a side note:
The following code can be problematic
public string ValueFullName
{
get { return (string)GetValue(ValueFullNameProperty); }
set
{
SetValue(ValueFullNameProperty, value);
Notify("ValueFullName"); // This line might never get called
}
}
This is just a CLR wrapper for the actual DependencyProperty and will never be invoked by the framework. When using the binding on this property the wrapper will never get called and therefore the event will never get raised.
As BionicCode has pointed out, you can change the AncestorType. Another option is to set DataContext of the Window.
You can either do it in the constructor
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
or in the XAML.
<Window DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}">
This way you don't have to specify source in your bindings (as long as you bind to code-behind properties).
I had this simple inventory manager in WPF using a datagrid hooked up to a table using SQL working the other day but changed a line of the code and now it's broken. I've slept since I broke it and can't work out how to fix it again.
The problem seems to be with with dataGrid1.Items.Add(testtables) from the MainWindow class below.
I ran it in a trycatch with the exception message saying that the operation is not valid while ItemsSource is in use, and that ItemsControl.ItemsSource should be used to access and modify elements instead.
Here's the code for the main form
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DataClasses1DataContext dc = new DataClasses1DataContext(Properties.Settings.Default.TestingConnectionString);
public MainWindow()
{
InitializeComponent();
if (dc.DatabaseExists())
{
dataGrid1.ItemsSource = dc.TestTables;
}
}
private void newButton_Click(object sender, RoutedEventArgs e)
{
Window1 window1 = new Window1();
window1.Show();
}
public void NewLine (int ID, string nm, decimal pc)
{
TestTable testtable = new TestTable
{
ID = ID,
Name = nm,
Price = pc
};
dataGrid1.Items.Add(testtable);
}
private void saveButton_Click_1(object sender, RoutedEventArgs e)
{
dc.SubmitChanges();
}
}
}
XAML code for MainWindow
<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button x:Name="newBtn" Content="New" HorizontalAlignment="Left" Margin="217,62,0,0" VerticalAlignment="Top" Width="75" Click="newButton_Click"/>
<Button x:Name="save" Content="Save" HorizontalAlignment="Left" Margin="401,82,0,0" VerticalAlignment="Top" Width="75" Click="saveButton_Click_1"/>
<DataGrid x:Name="dataGrid1" HorizontalAlignment="Left" Height="100" Margin="77,236,0,0" VerticalAlignment="Top" Width="575" IsReadOnly="True"/>
</Grid>
</Window>
and here's the code for the secondary form that asks the user to input the data
public partial class Window1 : Window
{
public int iDNumber;
public string name;
public decimal price;
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
iDNumber = Convert.ToInt32(iDTextBox.Text);
name = Convert.ToString(nameTextBox.Text);
price = Convert.ToDecimal(priceTextBox.Text);
((MainWindow)Application.Current.MainWindow).NewLine(iDNumber, name, price);
this.Close();
}
}
XAML code for Window1
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="Window1" Height="450" Width="400">
<Grid Margin="0,0,0,0">
<Button Content="Save" HorizontalAlignment="Left" Margin="156,362,0,0" VerticalAlignment="Top" Width="76" Click="Button_Click"/>
<TextBox x:Name="iDTextBox" HorizontalAlignment="Left" Height="23" Margin="156,79,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="nameTextBox" HorizontalAlignment="Left" Height="23" Margin="156,117,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="priceTextBox" HorizontalAlignment="Left" Height="23" Margin="156,155,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="ID" HorizontalAlignment="Left" Margin="95,79,0,0" VerticalAlignment="Top"/>
<Label Content="Name" HorizontalAlignment="Left" Margin="95,117,0,0" VerticalAlignment="Top"/>
<Label Content="Price" HorizontalAlignment="Left" Margin="95,155,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
When it was working the other day, it nicely added the new line of data to the SQL database (however didn't automatically show the updated info in the application, but this isn't the problem at the moment).
In this section of your code:
public MainWindow()
{
InitializeComponent();
if (dc.DatabaseExists())
{
dataGrid1.ItemsSource = dc.TestTables;
}
}
You are displaying the data by assigning the datatable to the datagrid.ItemsSource property. If you do this, you need to add items by modifying the DataTable instead of the DataGrid.
dataGrid1.Items.Add(testtable);
So instead of what's above, try adding the testtable item to your existing collection dc.TestTables
On initialization of main window i set DataContext to usercontrol and on this usercontrol i have an event which suppose to change datacontext of main window to another usercontrol but nothing happens.
Here is xaml for main window:
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}" Width="auto" Height="auto" />
</Grid>
Here is C# for main window:
public MainWindow()
{
InitializeComponent();
DataContext = new LogInViewModel();
}
Here is xaml for LogInUserControl:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="1" Grid.Row="1">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Width="250">
<StackPanel Width="125">
<TextBlock Text="Email:" Margin="5,0,5,0" Width="auto"/>
</StackPanel>
<StackPanel Width="125">
<TextBlock Text="Password:" Margin="5,0,0,0" Width="auto"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBox Margin="5,0,5,0" HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<PasswordBox Margin="0,0,0,5" HorizontalAlignment="Center" Height="23" VerticalAlignment="Top" Width="120"/>
</StackPanel>
<Button Content="Log In" Margin="0,0,0,5" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75"/>
</StackPanel>
<Grid Grid.Column="1" Grid.Row="1">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="don't have account yet ?" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="5"/>
<TextBlock Name="TBSignUp" Text="Sign Up" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="5" PreviewMouseLeftButtonDown="TextBlock_PreviewMouseLeftButtonDown" PreviewMouseLeftButtonUp="TextBlock_PreviewMouseLeftButtonUp" Foreground="#FF0B36F5"/>
</StackPanel>
</Grid>
</Grid>
and here is C# for LogInUserControl:
public partial class LogInView : UserControl
{
string BlackForeground = "#FF000000" ;
string OriginalForeground = "#FF0B36F5";
public LogInView()
{
InitializeComponent();
}
private void TextBlock_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TBSignUp.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(OriginalForeground));
DataContext = new RegisterView();
}
private void TextBlock_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
TBSignUp.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(BlackForeground));
}
}
In WPF you can get a shell (first) window from anywhere:
System.Windows.Window shell = System.Windows.Application.Current.MainWindow;
OR
Application.Current.Windows[0];
but I recommend passing the reference if needed.
look at the MVVM pattern, you need to implement property change notification in your view model class to bind properties.
You need to set it on App.xaml.cs.
App.xaml.cs:
public class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
MainWindow window=new MainWindow();
LogInViewModel vm=new LogInViewModel(); // You need to set DataContext...
window.DataContext=vm; // ...before showing up the window.
window.Show();
}
}
In the ViewModel patterns that I found from research, usage is before DataContext, after Show();.
I hope that solves your problem.
I am a newbie in WPF, I have a problem concern binding two different ViewModels to two UserControls
that will be attached to two Tabpages in a Tabcontrol.
My code snippets are as follows:
MainWindow.xaml
<Window.Resources>
<local:UserControl1Model x:Key="Control1Model" />
<local:UserControl2Model x:Key="Control2Model" />
</Window.Resources>
<Grid HorizontalAlignment="Left" Height="330" VerticalAlignment="Top" Width="592">
<Grid HorizontalAlignment="Left" Height="45" Margin="0,330,-1,-45" VerticalAlignment="Top" Width="593">
<Button Content="Button" HorizontalAlignment="Left" Margin="490,5,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
<TabControl HorizontalAlignment="Left" Height="330" VerticalAlignment="Top" Width="592" >
<TabItem x:Name="UserControl1TabItem" Header="User Control 1" >
<Grid x:Name="UserControl1Tabpage" Background="#FFE5E5E5" Margin="0,0,-4,-2" Height="300" VerticalAlignment="Top" IsEnabled="true" >
<local:UserControl1 VerticalAlignment="Top" DataContext="{Binding Source={StaticResource Control1Model}}" />
</Grid>
</TabItem>
<TabItem x:Name="UserControl2TabItem" Header="User Control 2">
<Grid x:Name="UserControl2Tabpage" Background="#FFE5E5E5">
<local:UserControl2 VerticalAlignment="Top" DataContext="{Binding Source={StaticResource Control2Model}}" />
</Grid>
</TabItem>
</TabControl>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private UserControl1Model _userControl1Model = new UserControl1Model();
private UserControl2Model _userControl2Model = new UserControl2Model();
public MainWindow()
{
InitializeComponent();
_userControl1Model.Message = "Hello";
_userControl2Model.Message = "Test";
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Will do something
}
}
UserControl1Model.cs
public class UserControl1Model : INotifyPropertyChanged
{
private string _message;
public string Message
{
get { return _message; }
set
{
_message = value;
OnPropertyChanged("Message");
}
}
public UserControl1Model()
{
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string message)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(message));
}
}
#region INotifyPropertyChanged Members
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
For trying purpose, the content of UserControl2Model.cs is as same as UserControl1Model.cs
UserControl1.xaml
<UserControl.Resources>
<app:UserControl1Model x:Key="Control1Model" />
</UserControl.Resources>
<Grid Margin="0,0,0,42" DataContext="{Binding Source={StaticResource Control1Model}}">
<Label Content="Test:" HorizontalAlignment="Left" Margin="48,57,0,0" VerticalAlignment="Top" Width="47"/>
<TextBox x:Name="Conrol1ModelTextbox" HorizontalAlignment="Left" Height="23" Margin="90,59,0,0" TextWrapping="Wrap"
Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" Width="466" />
</Grid>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
}
UserControl2.xaml
<UserControl.Resources>
<app:UserControl2Model x:Key="Control2Model" />
</UserControl.Resources>
<Grid Margin="0,0,0,42" DataContext="{Binding Source={StaticResource Control2Model}}">
<Label Content="Test:" HorizontalAlignment="Left" Margin="48,57,0,0" VerticalAlignment="Top" Width="47"/>
<TextBox x:Name="Conrol2ModelTextbox" HorizontalAlignment="Left" Height="23" Margin="90,59,0,0" TextWrapping="Wrap"
Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" Width="466" />
</Grid>
For trying purpose, the content of UserControl2.xaml.cs is as same as UserControl1.xaml.cs
My problem is the initial values, "Hello" and "Test" for the two user controls, which are initialized in MainWindow.xaml.cs cannot
be "binded" into the user controls textboxes. What am I doing wrong or missing?
When you declare resources like this
<Window.Resources>
<local:UserControl1Model x:Key="Control1Model" />
<local:UserControl2Model x:Key="Control2Model" />
</Window.Resources>
You are actually constructing new instances of UserControl1Model and UserControl2Model instead using the ones you declared in MainWindow.cs
Also you are not creating any ViewModel for the MainWindow. You should create a MainWindowViewModel like such
public class MainWindowViewModel : INotifyPropertyChanged
{
public ViewModelLocator
{
this.FirstModel= new UserControl1Model
{
Message = "Hello";
}
this.SecondModel = new UserControl2Model
{
Message = "Test";
}
}
private UserControl1Model firstModel
public UserControl1Model FirstModel
{
get
{
return this.firstModel;
}
set
{
this.firstModel= value;
OnPropertyChanged("FirstModel");
}
}
// Same for the UserControl2Model
// implementation of the INotifyPropertyChanged
}
Also you would need to set the DataContext for the MainWindow.
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
And remove the resources from the UserControl xamls. You are already defining the DataContext in the MainWindow.xaml but the binding should be bound from the MainWindowViewModel as such.
<local:UserControl1 VerticalAlignment="Top" DataContext="{Binding FirstModel}" />
Create a ViewModelLocator. you can find various sites on the internet for this subject.
a simple one would be:
public class ViewModelLocator
{
public UserControl1Model UserControl1Model { get; set; } = new UserControl1Model();
public UserControl2Model UserControl2Model { get; set; } = new UserControl2Model();
public ViewModelLocator
{
UserControl1Model.Message = "Hello";
UserControl2Model.Message = "Test";
}
}
then you can use it in your views
<UserControl.Resources>
<app:ViewModelLocator x:Key="ViewModelLocator" />
</UserControl.Resources>
<Grid Margin="0,0,0,42" DataContext="{Binding UserControl2Model Source={StaticResource ViewModelLocator}}">
<Label Content="Test:" HorizontalAlignment="Left" Margin="48,57,0,0" VerticalAlignment="Top" Width="47"/>
<TextBox x:Name="Conrol2ModelTextbox" HorizontalAlignment="Left" Height="23" Margin="90,59,0,0" TextWrapping="Wrap"
Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" Width="466" />
</Grid>
Suppose, I create this instance in the xaml as follows: <Window.Resources> <local:MainWindowViewModel x:Key="MainWindowViewModel" /> </Window.Resources> instead of creating inside MainWindow.xaml.cs. Then, how can I reference this instance inside MainWindow.xaml.cs, e.g. to get value from MainWindowViewModel.ViewModelLocator.FirstModel.Message ?
Like this:
MainWindowViewModel viewModel = this.Resources["MainWindowViewModel"] as MainWindowViewModel;
//access any properties of viewModel here...
Here's the code for my window:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="leartWPF.ControlTestWindow"
x:Name="Window"
Title="ControlTestWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<TextBlock Height="26" Margin="45,26,241,0" TextWrapping="Wrap" Text="Please, enter an ariphmetical expression to calculate:" VerticalAlignment="Top"/>
<TextBox Margin="48,72,63,201" TextWrapping="Wrap" Text="{Binding Input, ElementName=Window, FallbackValue=1+1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" TextChanged="TextBox_TextChanged" >
</TextBox>
<!--<TextBlock Margin="282,208,266,167" TextWrapping="Wrap" Text="=" FontSize="64"/>-->
<TextBlock Height="90" Margin="83,0,77,60" TextWrapping="Wrap" VerticalAlignment="Bottom" FontSize="48" Text="{Binding Result, ElementName=Window, Mode=TwoWay}"/>
<Button Content="=" Height="27" Margin="233,0,263,166" VerticalAlignment="Bottom" FontSize="16"/>
</Grid>
</Window>
and the class:
public partial class ControlTestWindow : Window
{
private string _input;
public double Result { get; set; }
private static VsaEngine _engine = VsaEngine.CreateEngine();
public string Input
{
get { return _input; }
set
{
Result = double.Parse(Eval.JScriptEvaluate(value, _engine).ToString());
_input = value;
}
}
public ControlTestWindow()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
}
}
The Input gets updated, and Result value changes, but it is never displayed on the appropriate TextBlock.
What should I change for this to work?
The TextBlock doesn't get notified of the change to the Result property. You have two options:
Implement the property as a DependencyProperty. Visual studio has a code snippet for it. Type propdp and you'll see it pop up in intellisense.
Implement INotifyPropertyChanged on your Window class and use it in your property.