How to assign property (get; set;) to a WPF User Control? - c#

I'm trying to learn property (get and set) in C#. I'm still new to the language and currently making a simple program using WPF with some textboxes, as you can see in the pictures.
So, here are the description :
Input : it's where user can type the input, located at MainWindow
Output 1 : it's where to see the typed string in input, located at MainWindow
Output 2 : same like Output 1, located inside tab one, at MainWindow.
Output 3 : same like Output 1, located inside tab two, still at MainWindow.
Output 4 : same like Output 1, located inside tab two, referred to local UserControl Page1.
Button : a button to "save" the input
Here are the codes :
Main Window : solution --> I added x:Name="Page1" after local:page1
<!-- MainWindow.xaml -->
<Window x:Class="TestGetSet.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:TestGetSet"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl x:Name="tabControl" HorizontalAlignment="Left" Height="212" Margin="37,20,0,0" VerticalAlignment="Top" Width="447">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<TextBox x:Name="output2" HorizontalAlignment="Left" Height="23" Margin="156,76,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<Label x:Name="label2" Content="Output 2" HorizontalAlignment="Left" Margin="156,46,0,0" VerticalAlignment="Top"/>
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<TextBox x:Name="output3" HorizontalAlignment="Left" Height="23" Margin="321,26,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<local:Page1 x:Name="Page1"/>
<Label x:Name="label3" Content="Output 3" HorizontalAlignment="Left" Margin="321,0,0,0" VerticalAlignment="Top"/>
</Grid>
</TabItem>
</TabControl>
<TextBox x:Name="input" Text=""/>
<TextBox x:Name="output1" Text=""/>
<Button x:Name="button" Content="Button" Click="button_Click"/>
<Label x:Name="label" Content="Input" HorizontalAlignment="Left" Margin="37,239,0,0" VerticalAlignment="Top"/>
<Label x:Name="label1" Content="Output 1" HorizontalAlignment="Left" Margin="364,239,0,0" VerticalAlignment="Top"/>
</Grid>
MainWindow code behind : solution --> added one line Page1.passingvalue(..)
// MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
public partial class MainWindow : Window
{
public myProperty myProp = new myProperty();
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
myProp.myData = input.Text;
output1.Text = myProp.myData;
output2.Text = myProp.myData;
output3.Text = myProp.myData;
Page1.passingvalue(myProp.myData);
}
}
Next is Page1.xaml (No changes made here for the solution)
<!-- Page1.xaml-->
<UserControl x:Class="TestGetSet.Page1"
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:TestGetSet"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox x:Name="output4" Text=""/>
<Label x:Name="label" Content="Output 4" HorizontalAlignment="Left" Margin="92,110,0,0" VerticalAlignment="Top"/>
</Grid>
</UserControl>
Page1 code behind : solution--> deleted timer and added passingvalue
// Page1.xaml.cs
using System;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Threading;
using System.ComponentModel;
namespace TestGetSet
{
public partial class Page1 : UserControl
{
private Thread _receiveThread;
myProperty myProp = new myProperty();
public Page1()
{
InitializeComponent();
/*DispatcherTimer MyTimer = new DispatcherTimer();
MyTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
MyTimer.Tick += MyTimer_Tick;
MyTimer.Start();*/
}
public void passingvalue(string m)
{
output4.Text = m;
}
/*private void MyTimer_Tick(object sender, EventArgs e)
{
output4.Text = myProp.myData;
}*/
}
}
Last one, the property, simple version :
// myProperty.cs
namespace TestGetSet
{
public class myProperty
{
public string myData { get; set }
}
}
Property with INotifyPropertyChanged :
// myProperty.cs
using System.ComponentModel;
namespace TestGetSet
{
public class myProperty : INotifyPropertyChanged
{
private string _textdata;
public string myData {
get
{
return _textdata;
}
set
{
_textdata = value;
NotifyPropertyChanged("myData");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here are the screenshots of the program window :
one
and
two
As you can see, only Output4 is empty and the other outputs still give me results even though I don't use INotifyPropertyChanged. My question is why, and how can I fix this? I wonder if it's because I'm using UserControl for Page1, which the Output4 is in. I've been looking for the answer but came up with nothing. Any help is much appreciated. Thank you.
Okay so I updated the code. It's working now. I got the reference for passingvalue from : How to Pass a Value From a Window to a UserControl in WPF
Thank you.

Your issue here is that you misstakes the MainWindow's myProperty and Page1's myProperty.
In Page1, you set the output4.Text using myProp.myData which is wrong because myProp is the Page1's myProp which is never updated.
If you pass the MainWindow's reference to Page1 and instead write something like output4.Text = myMainWindowReferenc.myProp.myData; it will work as intended.
You could also add setting to output4 in your button_Click function by naming the Page1 in the XAML:
private void button_Click(object sender, RoutedEventArgs e)
{
myProp.myData = input.Text;
output1.Text = myProp.myData;
output2.Text = myProp.myData;
output3.Text = myProp.myData;
Page1.output4.Text = myProp.myData;
}

Related

Textbox not updating when static object method to update property is called

First, let me state I am new to C# altogether. At the moment I am working on a program, and have been trying to get something that I thought would be rather simple to do. This is apparently not the case. I'll show here a very small example of what I have been trying to do. This is functionally identical, but without all the extra things that I have in the real project. We have 3 files here.
MainWindow.xaml
<Window.Resources>
<CollectionViewSource x:Key="data1ViewSource" d:DesignSource="{d:DesignInstance {x:Type local:Data1}, CreateList=True}"/>
</Window.Resources>
<Grid>
<Button x:Name="TestButton" Content="Button" HorizontalAlignment="Left" Margin="78,25,0,0" VerticalAlignment="Top" Width="75" Click="TestButton_Click"/>
<TextBox x:Name="testStringTextBox" HorizontalAlignment="Left" Height="24" Margin="78,86,0,309" Grid.Row="0" Text="{Binding TestString}" VerticalAlignment="Center" Width="120"/>
<TextBlock x:Name="testStringTextBlock" HorizontalAlignment="Left" Margin="78,143,0,0" TextWrapping="Wrap" Text="{Binding TestString}" VerticalAlignment="Top" Height="181" Width="245"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TestButton_Click(object sender, RoutedEventArgs e)
{
Class1.TheButton();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource data1ViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("data1ViewSource")));
// Load data by setting the CollectionViewSource.Source property:
// data1ViewSource.Source = [generic data source]
}
}
Class1.cs
public static class Class1
{
private static int Inc = 1;
public static Data1 testData = new Data1("Starting test\n");
public static void TheButton()
{
testData.TestString += "Test number " + Inc.ToString() + ".\n";
Inc++;
}
}
public class Data1 : INotifyPropertyChanged
{
public Data1(string input)
{
TestString = input;
}
private string _testString;
public event PropertyChangedEventHandler PropertyChanged;
public string TestString
{
get { return _testString; }
set { _testString = value; OnPropertyChanged("TestString"); }
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
The program compiles as is, without error, and when I set a breakpoint, I can view the object Class1.testData, and I can see the proper value in Class1.testData.TestString and _testString.
I have spent several days searching for some sort of simple example of what I'm trying to do here, and I have yet been able to find any solution to get it actually working. I've come to the conclusion that I must be doing something wrong or just plain missing something, but I also cannot find anything to give me insight on what that is.
Change the xaml to use x:Static and provide the Path:
<Canvas>
<Button x:Name="TestButton" Content="Button" HorizontalAlignment="Left" Margin="78,25,0,0" VerticalAlignment="Top" Width="75" Click="TestButton_Click"/>
<TextBox x:Name="testStringTextBox"
HorizontalAlignment="Left"
Height="24"
Margin="78,86,0,309"
Text="{Binding Source={x:Static local:Class1.testData}, Path=TestString}"
VerticalAlignment="Center"
Width="120"/>
<TextBlock x:Name="testStringTextBlock"
HorizontalAlignment="Left"
Margin="78,143,0,0"
TextWrapping="Wrap"
Text="{Binding Source={x:Static local:Class1.testData}, Path=TestString}"
VerticalAlignment="Top"
Height="181"
Width="245"/>
</Canvas>
Also note the if you enter a value in the textbox, the value will only be updated in your variable when the textbox loses focus.

WPF pass ListView selected items to another ListView

I have a list view with some items in the main window. Then I add checked boxes so that when an item is selected it also get checked.
Now I tried to pass the selected items from that list view to another list view but here is what I get:
Here is my XAML:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="497">
<TabControl Height="279" Canvas.Left="10" Canvas.Top="10" Width="477">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<ListView Name="lv1" Margin="0,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="110" Width="471">
<ListViewItem>
<CheckBox >
<StackPanel Orientation="Horizontal">
<TextBlock Text="Apple"/>
</StackPanel>
</CheckBox>
</ListViewItem>
<ListViewItem>
<CheckBox >
<StackPanel Orientation="Horizontal">
<TextBlock Text="Orange"/>
</StackPanel>
</CheckBox>
</ListViewItem>
</ListView>
<Button Content="Copy" Width="100" Height="25" Click="Button_Click"/>
<ListView Name="lv2" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="110" Width="471"/>
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</Canvas>
</Grid>
Here is code behind in c#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
List<string> lv1list = new List<string>();
foreach (var i in lv1.SelectedItems)
{
lv1list.Add(i.ToString());
}
lv2.Items.Add(lv1.SelectedItems);
}
}
}
What went wrong here?
The problem is you're adding a complete list as an item which is why you're getting the (Collection) value.
What you can do is get all the selected item in a list them loop and add them one by one
private void Button_Click(object sender, RoutedEventArgs e)
{
var selectedItems = lv1.SelectedItems;
for(int i = 0; i < selectedItems.Count; i++)
{
lv2.Items.Add(selectedItems[i]);
}
}
You might also want to clear your lv2 before adding the new values
lv2.Items.Clear();
Another option which wouldn't require you to press a button so that the values appear in the second listview would be to bind the ItemsSource of your lv2 to the SelectedItems of your lv1
lv2.ItemsSource = lv1.SelectedItems;
You can do that once at the beginning and lv2 will always contain the selected items of lv1 and will update as soon as the selected items changes.
I made some more modifications and then I realized it is not working as it should. I have a "foreach statement" looping through all the checked/selected items in listBox1, then inside the "foreach statement" there is an "If statement" to check if the checked/selected items from listBox1 are not in listBox2, if they aren't then they are copied to listBox2. Each condition of the "If statement" should display relevant MessageBox. The problem now is that the "If statement" doesn't work properly. I know this, because I cannot see the correct relevant MessageBox to the correct condition of the "If statement". However the items are not duplicated, meaning they are not being copied over and over only they appear duplicated in the MessageBox. Here is my code and hope someone can spot my mistake
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System;
namespace MYNAMESPACE
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
// To initialise the List and use it outside the class
//MyListItem instanceOfClass = new MyListItem();
//List<CheckBoxListItem> listOfItems = instanceOfClass.MyMethod();
//listBox1.ItemsSource = listOfItems;
}
public class CheckBoxListItem : INotifyPropertyChanged
{
public bool CheckStatus { get; set; }
public string Text { get; set; }
public CheckBoxListItem(bool _CheckStatus, string _Text)
{
CheckStatus = _CheckStatus;
Text = _Text;
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MyListItem
{
public List<CheckBoxListItem> MyMethod()
{
List<CheckBoxListItem> items = new List<CheckBoxListItem>();
items.Add(new CheckBoxListItem(false, "Item 1"));
items.Add(new CheckBoxListItem(false, "Item 2"));
items.Add(new CheckBoxListItem(false, "Item 3"));
return items;
}
}
public List<string> selectedNames = new List<string>();
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var checkBox = sender as CheckBox;
var item = checkBox.Content;
bool isChecked = checkBox.IsChecked.HasValue ? checkBox.IsChecked.Value : false;
if (isChecked)
selectedNames.Add(item.ToString());
else
selectedNames.Remove(item.ToString());
}
public string selections;
bool updatedItems;
public void Button_Click(object sender, RoutedEventArgs e)
{
foreach (string selection in selectedNames)
{
selections += selection + Environment.NewLine;
if (!listBox2.Items.Contains(selection))
{
listBox2.Items.Add(selection);
updatedItems = true;
}
else if (listBox2.Items.Contains(selection))
{
updatedItems = false;
}
}
if (updatedItems == true)
MessageBox.Show("Add items are: " + selections);
else if (updatedItems == false)
MessageBox.Show("No update to selection was made.");
}
private void CheckStatus(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}

Dynamic user control change - WPF

I'm developing an app in WPF and I need to change in runtime a content of a ContentControl depending than the user selected on ComboBox.
I have two UserControls and at my combo exists two itens, corresponding each one each.
First usercontrol:
<UserControl x:Class="Validator.RespView"
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="167" d:DesignWidth="366" Name="Resp">
<Grid>
<CheckBox Content="CheckBox" Height="16" HorizontalAlignment="Left" Margin="12,12,0,0" Name="checkBox1" VerticalAlignment="Top" />
<ListBox Height="112" HorizontalAlignment="Left" Margin="12,43,0,0" Name="listBox1" VerticalAlignment="Top" Width="168" />
<Calendar Height="170" HorizontalAlignment="Left" Margin="186,0,0,0" Name="calendar1" VerticalAlignment="Top" Width="180" />
</Grid>
Second usercontrol:
<UserControl x:Class="Validator.DownloadView"
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="76" d:DesignWidth="354" Name="Download">
<Grid>
<Label Content="States" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="12,35,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" />
<RadioButton Content="Last 48 hs" Height="16" HorizontalAlignment="Left" Margin="230,42,0,0" Name="rdbLast48" VerticalAlignment="Top" />
<Label Content="Kind:" Height="28" HorizontalAlignment="Left" Margin="164,12,0,0" Name="label2" VerticalAlignment="Top" />
<RadioButton Content="General" Height="16" HorizontalAlignment="Left" Margin="165,42,0,0" Name="rdbGeral" VerticalAlignment="Top" />
</Grid>
At MainWindowView.xaml
<Window x:Class="Validator.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:du="clr-namespace:Validator.Download"
xmlns:resp="clr-namespace:Validator.Resp"
Title="Validator" Height="452" Width="668"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<Window.Resources>
<DataTemplate DataType="{x:Type du:DownloadViewModel}">
<du:DownloadView/>
</DataTemplate>
<DataTemplate DataType="{x:Type resp:RespViewModel}">
<resp:RespView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding Path=PagesName}"
SelectedValue="{Binding Path=CurrentPageName}"
HorizontalAlignment="Left" Margin="251,93,0,0"
Name="cmbType"
Width="187" VerticalAlignment="Top" Height="22"
SelectionChanged="cmbType_SelectionChanged_1" />
<ContentControl Content="{Binding CurrentPageViewModel}" Height="171" HorizontalAlignment="Left" Margin="251,121,0,0" Name="contentControl1" VerticalAlignment="Top" Width="383" />
</Grid>
</Window>
I assigned to the DataContext of the MainView, the viewmodel below:
public class MainWindowViewModel : ObservableObject
{
#region Fields
private ICommand _changePageCommand;
private ViewModelBase _currentPageViewModel;
private ObservableCollection<ViewModelBase> _pagesViewModel = new ObservableCollection<ViewModelBase>();
private readonly ObservableCollection<string> _pagesName = new ObservableCollection<string>();
private string _currentPageName = "";
#endregion
public MainWindowViewModel()
{
this.LoadUserControls();
_pagesName.Add("Download");
_pagesName.Add("Resp");
}
private void LoadUserControls()
{
Type type = this.GetType();
Assembly assembly = type.Assembly;
UserControl reso = (UserControl)assembly.CreateInstance("Validator.RespView");
UserControl download = (UserControl)assembly.CreateInstance("Validator.DownloadView");
_pagesViewModel.Add(new DownloadViewModel());
_pagesViewModel.Add(new RespViewModel());
}
#region Properties / Commands
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return _changePageCommand;
}
}
public ObservableCollection<string> PagesName
{
get { return _pagesName; }
}
public string CurrentPageName
{
get
{
return _currentPageName;
}
set
{
if (_currentPageName != value)
{
_currentPageName = value;
OnPropertyChanged("CurrentPageName");
}
}
}
public ViewModelBase CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
#endregion
#region Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
int indexCurrentView = _pagesViewModel.IndexOf(CurrentPageViewModel);
indexCurrentView = (indexCurrentView == (_pagesViewModel.Count - 1)) ? 0 : indexCurrentView + 1;
CurrentPageViewModel = _pagesViewModel[indexCurrentView];
}
#endregion
}
On MainWindowView.xaml.cs, I wrote this event to do the effective change:
private void cmbType_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
MainWindowViewModel element = this.DataContext as MainWindowViewModel;
if (element != null)
{
ICommand command = element.ChangePageCommand;
command.Execute(null);
}
}
The app run ok and I inspected the application with WPFInspector and saw that the view changes when the combobox is changed internally, but the ContentControl still empty visually..
Sorry about the amount of code that I posted and my miss of knowledge but I'm working with this a long time and can't solve this problem.
Thanks
Issues:
Firstly don't ever create View related stuff in the ViewModel (UserControl). This is no longer MVVM when you do that.
Derive ViewModels from ViewModelBase and not ObservableObject unless you have a compelling reason to not use ViewModelBase when using MVVMLight. Keep ObservableObject inheritence for Models. Serves as a nice separation between VM's and M's
Next you do not need to make everything an ObservableCollection<T> like your _pagesViewModel. You do not have that bound to anything in your View's so it's just a waste. Just keep that as a private List or array. Check what a type actually does in difference to a similar other one.
Not sure about this one, maybe you pulled this code snippet as a demo, but do not use margins to separate items in a Grid. Your Layout is essentially just 1 Grid cell and the margins have the items not overlap. If you're not aware of that issue, Check into WPF Layout Articles.
Please don't forget principles of OOP, Encapsulation and sorts when writing a UI app. When having Properties like CurrentPageViewModel which you don't intend the View to switch make the property setter private to enforce that.
Don't resort to code-behind in the View too soon. Firstly check if it's only a View related concern before doing so. Am talking about your ComboBox SelectionChanged event handler. Your purpose of that in this demo is to switch the Bound ViewModel which is held in the VM. Hence it's not something that the View is solely responsible for. Thus look for a VM involved approach.
Solution:
You can get a working example of your code with the fixes for above from Here and try it out yourself.
Points 1 -> 5 are just basic straightforward changes.
For 6, I've created a SelectedVMIndex property in the MainViewModel which is bound to the SelectedIndex of the ComboBox. Thus when the selected index flips, the property setter after updating itself updates the CurrentPageViewModel as well such as
public int SelectedVMIndex {
get {
return _selectedVMIndex;
}
set {
if (_selectedVMIndex == value) {
return;
}
_selectedVMIndex = value;
RaisePropertyChanged(() => SelectedVMIndex);
CurrentPageViewModel = _pagesViewModel[_selectedVMIndex];
}
}

Data Binding between two TextBoxes in different windows

I have created a program that changes the name in a TextBox when check or unckeck a Checkbox. I want to replicate this Textbox in a different window. I thought with Data Mining in the xaml would be possible, but the name only appears in one window. Second window window does´t recieve the data. I show you the code of the two windows.
Can you help me? Thankss
Window 1.cs---
namespace WpfApplication1
{
public partial class Window1 : Window
{
Texto prueba = new Texto("Carlos");
public static string s;
public Window1()
{
InitializeComponent( );
// Fill initial person fields
this.textBox1.Text = prueba.Name;
}
private void checkBox1_Checked(object sender, RoutedEventArgs e)
{
prueba.Name="Carlos";
textBox1.DataContext = prueba;
textBox1.Text = prueba.Name;
}
private void checkBox1_UnChecked(object sender, RoutedEventArgs e)
{
prueba.Name = "Luis";
textBox1.DataContext = prueba;
textBox1.Text = prueba.Name;
}
}
public class Texto
{
string name;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public Texto( ) {}
public Texto(string name)
{
this.name = name;
}
}
}
window1 xaml-------
<Grid>
<CheckBox Content="CheckBox" Height="16" HorizontalAlignment="Left" Margin="62,118,0,0" Name="checkBox1" VerticalAlignment="Top" Checked="checkBox1_Checked" Unchecked="checkBox1_UnChecked" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="44,140,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
window2 cs-----
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Window1 nueva = new Window1();
nueva.Show();
}
}
}
window2 xaml--------
<Grid>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="82,121,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox DataContext="prueba" Text="{Binding Path=Name}" Height="23" HorizontalAlignment="Left" Margin="57,84,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
</Grid>
You will have to pass a reference to either the first window or the object that you're updating the text property on to the second window, it's DataContext property will do for that, you can then bind the second windows controls to it.
In this demo application I've created a MainWindow and a second window (Window1), the application starts in Main window like this.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public string TestString
{
get { return (string)GetValue(TestStringProperty); }
set { SetValue(TestStringProperty, value); }
}
public static readonly DependencyProperty TestStringProperty = DependencyProperty.Register("TestString", typeof(string), typeof(MainWindow), new UIPropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
// setup the test string.
TestString = "this is a test.";
// Create the second window and pass this window as it's data context.
Window1 newWindow = new Window1()
{
DataContext = this
};
newWindow.Show();
}
}
MainWindow.xaml - Take note of the DataContext line in the Window declaration.
<Window x:Class="WpfApplication5.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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Grid>
<TextBox Text="{Binding TestString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="91,84,185,189" />
</Grid>
</Window>
Now for Window1 the code behind is just an empty default window class so I won't post it but the xaml is.
Window1.xaml
<Window x:Class="WpfApplication5.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>
<TextBlock Text="{Binding TestString, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
Don't set the DataContext explicitly, or only via another Binding. Your
<TextBox DataContext="prueba"
does nothing helpful. DataContext will be inherited as long as it is not overwritten. Don't set it explicitly. It should be enought to set it once on both windows.
Create your data object in your MainWindow
Texto prueba = new Texto("Carlos");
Window1 nueva = new Window1();
nueva.DataContext = prueba;
nueva.Show();
and remove all the other DataContext assignments.
A couple of things wrong here, but I can probably give you a quick solution to fix your problem. First off your DataContext on window 2 isn't working properly, you can set it exclusively in your code right before you show window1...
private void button1_Click(object sender, RoutedEventArgs e)
{
Window1 nueva = new Window1();
this.DataContext = nueva.prueba;
nueva.Show();
}
Next you have to fire INotifyPropertyChanged in your Texto class...
public class Texto : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public Texto( ) {}
public Texto(string name)
{
this.name = name;
}
}
If both textboxes share a common datacontext it will "just work" without any code required...

Use created DLL in a Wpf window?

I have a DLL with the following code
using System.Text;
using Microsoft.Win32;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ApplicationCheck
{
public class ApCkr
{
#region .NET
public string Netframeworkavailable()
{
bool NETinstall;
RegistryKey k1 = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Client");
if (k1 == null)
{
NETinstall = false;
}
else
{
NETinstall = true;
}
return NETinstall.ToString();
}
#endregion
#region PDF
public string PDFavailable()
{
bool PDFinstall;
RegistryKey k2 = Registry.ClassesRoot.OpenSubKey(".pdf");
if (k2 == null)
{
PDFinstall = false;
}
else
{
PDFinstall = true;
}
return PDFinstall.ToString();
}
#endregion
#region IExplore
public string IEavailable()
{
bool IEversion;
string k3 = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Internet Explorer").GetValue("Version").ToString();
string z = k3.Substring(0, 1);
int a = Int32.Parse(z);
if (a < 8)
{
IEversion = false;
}
else
{
IEversion = true;
}
return IEversion.ToString();
}
#endregion
#region IIS
public string IISavailable()
{
bool IISinstall;
RegistryKey k4 = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\InetStp");
if (k4 == null)
{
IISinstall = false;
}
else
{
IISinstall = true;
}
return IISinstall.ToString();
}
#endregion
}
}
And a WPF window with the followig XAML code
<Window x:Class="WpfApplication1.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ResizeMode="CanResizeWithGrip"
WindowStyle="None" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Title="Window2" Height="350" Width="525">
<Grid>
<Label Content="Windows" Height="25" HorizontalAlignment="Left" Margin="12,15,0,0" Name="label1" VerticalAlignment="Top" Width="106" />
<Label Content="Edition " Height="25" HorizontalAlignment="Left" Margin="12,45,0,0" Name="label2" VerticalAlignment="Top" Width="106" />
<Label Content="Service Pack " Height="25" HorizontalAlignment="Left" Margin="12,75,0,0" Name="label3" VerticalAlignment="Top" Width="106" />
<Label Content="Version " Height="25" HorizontalAlignment="Left" Margin="12,105,0,0" Name="label4" VerticalAlignment="Top" Width="106" />
<Label Content="Processor Bits " Height="25" HorizontalAlignment="Left" Margin="12,135,0,0" Name="label5" VerticalAlignment="Top" Width="106" />
<Label Content="OS Bits " Height="25" HorizontalAlignment="Left" Margin="12,165,0,0" Name="label6" VerticalAlignment="Top" Width="106" />
<Label Content="Program Bits " Height="25" HorizontalAlignment="Left" Margin="12,195,0,0" Name="label7" VerticalAlignment="Top" Width="106" />
<TextBlock Height="21" HorizontalAlignment="Left" Margin="114,19,0,0" Name="textBlock1" Text="{Binding Path=var}" VerticalAlignment="Top" Width="249" ContextMenuOpening="textBlock1_ContextMenuOpening" />
</Grid>
</Window>
and the WPF's c# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
}
private void textBlock1_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var NET = new ApplicationCheck.ApCkr();
textBlock1.Text = NET.Netframeworkavailable();
this.DataContext = textBlock1;
}
}
}
I researched data binding in MSDN and At stack overflow namely this - DataBinding Between a WPF GUI and a couple of ListBox/CheckBox
and others but i cannot get it right.And although stack overflow helped me utilise this in a console app.Now i have to do this in a WPF window.
Edit:I have to display the returned values from the DLL
In order to present the data from the DLL in the UI using binding, you need to have an object with public getters. In your UI DLL create a class (in mvvm design patern, this class is called 'View Model') with the public getters:
public class ApCkrVm {
public string netFrameworkAvailable {
get { return ApCkr.NetFrameworkAvailable(); }
}
public string pdfAvailable {
get { return ApCkr.PDFAvailable(); }
}
...
}
Then, in Window2 constructor, set ApCkrVm to be the DataContext:
public Window2( ) {
this.DataContext = new ApCkrVm( );
InitializeComponent( );
}
Finally, add text blocks in the XML file, binding the Text to the properties:
<TextBlock Text="{Binding Path=netFrameworkAvailable}" ... />
Some other comments:
You aren't utilizing very well the <Grid> element. You'll be better off defining ColumnDefinitions and RowDefinitions, creating a 2xn table.
ApCkr method can all be static. This class has no context.
I don't think your application could run if .net framework isn't available. If your application is running, you can safely put 'true' there.
Consider caching the values in ApCkrVm.
First add that Dll in your resources after that write following code in your wpf form xaml side.
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="220" Width="343"
xmlns:my="clr-namespace:TestWpfControlLibrary;assembly=TestWpfControlLibrary" Left="Auto" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" WindowStyle="None">
this following last line is impotent. it decide you use the particular dll.
xmlns:my="clr-namespace:TestWpfControlLibrary;assembly=TestWpfControlLibrary"`
after that in xaml page within grid teg use define "my", whome you use in xmlns:my
for that use following code.
<my:UserControl1 Height="168" HorizontalAlignment="Left" Margin="10,22,0,0" VerticalAlignment="Top" Width="307" Name="login" />
after that go to form.cs page and write the following code in pageload method
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
login.Visibility = System.Windows.Visibility.Visible;
}
}
now your dll is working properly.
you can download a test application like this from following link
http://www.dotnetfoundation.somee.com/Style/DesktopApp/WPF TEST.zip

Categories