I have the following WPF XAML:
<Window x:Class="ListBoxTesting.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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="ListBoxOne" Grid.Row="0" ItemsSource="{Binding ListOne}" DisplayMemberPath="NameOne"/>
<ListBox Name="ListBoxTwo" Grid.Row="1" ItemsSource="{Binding ListTwo}" DisplayMemberPath="NameTwo"/>
</Grid>
</Window>
With this code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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 ListBoxTesting
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class TypeOne
{
public string NameOne { get; set; }
public TypeOne(string name)
{
NameOne = name;
}
}
public class TypeTwo
{
public string NameTwo { get; set; }
public TypeTwo(string name)
{
NameTwo = name;
}
}
public ObservableCollection<TypeOne> ListOne { get; set; }
public ObservableCollection<TypeTwo> ListTwo { get; set; }
public MainWindow()
{
InitializeComponent();
ListOne = new ObservableCollection<TypeOne>();
ListOne.Add(new TypeOne("Mike"));
ListOne.Add(new TypeOne("Bobby"));
ListOne.Add(new TypeOne("Joe"));
ListTwo = new ObservableCollection<TypeTwo>();
ListTwo.Add(new TypeTwo("Mike"));
ListTwo.Add(new TypeTwo("Bobby"));
ListTwo.Add(new TypeTwo("Joe"));
}
}
}
But for some reason I can't see any rows in my UI once I start the project up in debug mode. Any ideas why?
You need to set DataContext to code behind to resolve your bindings:
May be in code behind:
public MainWindow()
{
InitializeComponent();
DataContext = this; <--- HERE
.....
}
OR in XAML:
<Window x:Class="ListBoxTesting.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}}"> <-- HERE
Related
I have been all over the internet looking for a clear answer and cannot find one. I have created Window Forms apps in the past, and decided to give WPF a try. I am hoping I don't have to move back to WinForms, but cannot figure out the simplest tasks.
How do I pass text in Page1's Textbox to Page2's TextBox.
I apologize if this is a duplicate, but I cannot find a clear answer anywhere. Most beginner mvvm tutorials seem to stick to one page apps.
MainWindo.xaml
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Frame Source="/Page1.xaml"/>
</Grid>
</Window>
Page1 Xaml:
<Page x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1"
xmlns:vm="clr-namespace:WpfApp3">
<Page.DataContext>
<vm:Page1ViewModel/>
</Page.DataContext>
<Grid>
<StackPanel>
<TextBlock Margin="20" FontSize="36">Welcome Home</TextBlock>
<TextBox x:Name="UserName" Margin="10 0 10 0" Text=""/>
<Button x:Name="Next" Margin="10 10" Content="Next" Click="Next_Click"/>
</StackPanel>
</Grid>
</Page>
Page1 Code-Behind:
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp3
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
private void Next_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(
new Uri("/Page2.xaml", UriKind.Relative));
}
}
}
Page1 ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp3
{
class Page1ViewModel : Notifier
{
private string username;
public string UserName
{
get { return username; }
set
{
username = value;
OnPropertyChanged("UserName");
}
}
}
}
Page2 Xaml:
<Page x:Class="WpfApp3.Page2"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page2"
xmlns:vm="clr-namespace:WpfApp3>
<Page.DataContext>
<vm:Page2ViewModel/>
<Page.Data.Context>
<Grid>
<StackPanel>
<TextBlock Margin="10">Hello there: </TextBlock>
<TextBox x:Name="TextBox_Name" Margin="10" Text="{Binding UserName}"/>
</StackPanel>
</Grid>
</Page>
Page2 Code-behind:
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp3
{
/// <summary>
/// Interaction logic for Page2.xaml
/// </summary>
public partial class Page2 : Page
{
public Page2()
{
InitializeComponent();
}
}
}
Page2 View Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp3
{
class Page2ViewModel : Notifier
{
private string textbox_name;
public string TextBox_Name
{
get { return textbox_name; }
set
{
textbox_name = value;
OnPropertyChanged("TextBox_Name");
}
}
}
}
first of all you don't need to move back to WinForms: you can avoid MVVM alltogether and use the traditional code-behind coding model you know even with WPF. I'd suggest doing so for your first WPF applications if you don't feel at ease with MVVM.
As for your problem, a solution could be to use static properties for common values storage.
public class MyCommonValues
{
public static string SharedText { get; set; }
}
class Page1ViewModel : Notifier
{
private string username;
public string UserName
{
get { return username; }
set
{
username = value;
OnPropertyChanged("UserName");
OnUserNameChanged(); // added to your code
}
}
// following is an addition to your code
void OnUserNameChanged()
{
MyCommonValues.SharedText = UserName;
}
}
class Page2ViewModel : Notifier
{
// following is an addition to your code
public Page2ViewModel()
{
TextBox_Name = MyCommonValues.SharedText;
}
private string textbox_name;
public string TextBox_Name
{
get { return textbox_name; }
set
{
textbox_name = value;
OnPropertyChanged("TextBox_Name");
}
}
}
I'm trying to create UserControls to be able to reuse them later in the WPF application. I choose to build a small project to train myself, but I can't make it work.
The objective is to have a TextBox whose content will be sent as a Label Text with the click on a Button.
I've read and tried the solutions on those links :
XAML Binding on Dependency Property
Custom Dependency Properties
Simple Dependency Property and UserControl issues in C#
Add dependency property to control
But even the starting text I set in the constructor doesn't show up, and the button click does nothing at all.
Here are my files :
MyControl.xaml
<UserControl x:Class="WpfApplication1.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"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Name="control"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Text="{Binding TextBoxContent,ElementName=control}"/>
<Button Content="Print Entry" Grid.Row="1" Command="{Binding ButtonCommmand,ElementName=control}"/>
<Label Grid.Row="2" Content="{Binding LabelContent,ElementName=control}"/>
</Grid>
</UserControl>
MyControl.xaml.cs
using GalaSoft.MvvmLight.CommandWpf;
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 WpfApplication1
{
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
ButtonCommmand = new RelayCommand(Action);
}
public string TextBoxContent
{
get
{
return (string)GetValue(TextBoxContentProperty);
}
set
{
SetValue(TextBoxContentProperty, value);
}
}
public RelayCommand ButtonCommmand
{
get
{
return (RelayCommand)GetValue(ButtonCommandProperty);
}
set
{
SetValue(ButtonCommandProperty, value);
}
}
public string LabelContent {
get
{
return (string)GetValue(LabelContentProperty);
}
set
{
SetValue(LabelContentProperty, value);
}
}
public void Action()
{
LabelContent = TextBoxContent;
}
public static readonly DependencyProperty TextBoxContentProperty = DependencyProperty.Register("TextBoxContent", typeof(string), typeof(MyControl), new PropertyMetadata(""));
public static readonly DependencyProperty ButtonCommandProperty = DependencyProperty.Register("ButtonCommmand", typeof(RelayCommand), typeof(MyControl), new PropertyMetadata(null));
public static readonly DependencyProperty LabelContentProperty = DependencyProperty.Register("LabelContent", typeof(string), typeof(MyControl), new PropertyMetadata(""));
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.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:WpfApplication1"
xmlns:control="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<control:MyControl DataContext="{Binding customControl}" TextBoxContent="{Binding Text,Mode=TwoWay}" LabelContent="{Binding EndText,Mode=TwoWay}" ButtonCommmand="{Binding Command,Mode=TwoWay}"/>
</Grid>
</Window>
MainWindow.xaml.cs
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
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 WpfApplication1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
MyControl customControl = new MyControl();
public MainWindow()
{
InitializeComponent();
Command = new RelayCommand(Action);
Text = "Testing... Testing... 1, 2, 3,...";
}
private string text;
public string Text
{
get
{
return text;
}
set
{
text = value;
NotifyPropertyChanged();
}
}
private string endText;
public string EndText
{
get
{
return endText;
}
set
{
endText = value;
NotifyPropertyChanged();
}
}
private RelayCommand command;
public RelayCommand Command
{
get
{
return command;
}
set
{
command = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void Action()
{
EndText = Text;
}
private void NotifyPropertyChanged([CallerMemberName]string PropertyName = "")
{
if (!String.IsNullOrEmpty(PropertyName))
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
}
Thank you a lot for your help !
You are creating another instance of your UserControl in the code-behind of the window:
MyControl customControl = new MyControl();
What you want to do is to bind the properties of the UserControl that you have defined in your XAML to the properties of the window. For this to work you should set the DataContext of the window to itself:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
Command = new RelayCommand(Action);
Text = "Testing... Testing... 1, 2, 3,...";
}
}
...and remove this from your XAML:
DataContext="{Binding customControl}"
I am trying to create a simple GUI application which changes the value of a string when it is changed in the GUI interface. The cs code is as follows:
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 TryBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
consle myconsole = new consle();
}
public class consle
{
public string mainstr {get; set;}
private int change = 23;
public void consle()
{
}
public void wrStr()
{
change = 44;
}
}
}
and the XAML code is as follows:
<Window x:Class="TryBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryBinding"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:consle x:Name="Data" mainstr="maintry"/>
</Window.DataContext>
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="244,37,0,0" VerticalAlignment="Top" Width="75"/>
<TextBlock HorizontalAlignment="Left" Margin="131,43,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Path=mainstr}" />
<CheckBox x:Name="ok" Content="CheckBox" HorizontalAlignment="Left" Margin="131,76,0,0" VerticalAlignment="Top"/>
</Grid>
but I am getting the error:
Error 1 The name "consle" does not exist in the namespace "clr-namespace:TryBinding".
Error 2 'consle': member names cannot be the same as their enclosing type
I am not able to understand why its happening since the class consle exists in the namespace, and the consle() is a constructor.
your constructor shoule look like
public consle()
{
}
Constructor doesn't have the datatype.
in Wpf application i've to show the same collection of items into a ListBox and TreeView, The List show only the first level, the TreeView the whole Hierarchical. My Question is: why in the TreeView is not applied the DataTemplate i declared for Item object? how do i can share the same DataTemplate across the 2 controls (the color of text into TV must be red)?
<Window x:Class="TestWpf.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"
xmlns:viewmodel="clr-namespace:TestWpf"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.Resources>
<DataTemplate DataType="{x:Type viewmodel:Item}">
<TextBlock Foreground="Red" Text="{Binding Caption}"></TextBlock>
</DataTemplate>
</Grid.Resources>
<ListBox Grid.Row="0" Name="lb1" ></ListBox>
<TreeView Name="tv1" Grid.Row="1" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewmodel:Item}" ItemsSource="{Binding Items}" >
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
MainWindow.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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.Navigation;
using System.Windows.Shapes;
namespace TestWpf
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<Item> Items = new ObservableCollection<Item>();
var item1 = new Item("1");
Items.Add(item1);
item1.Items.Add(new Item("1.1"));
item1.Items.Add(new Item("1.2"));
item1.Items.Add(new Item("1.3"));
var item2 = new Item("2");
Items.Add(item2);
item2.Items.Add(new Item("2.1"));
item2.Items.Add(new Item("2.2"));
item2.Items.Add(new Item("2.3"));
var item3 = new Item("3");
Items.Add(item3);
item3.Items.Add(new Item("3.1"));
item3.Items.Add(new Item("3.2"));
item3.Items.Add(new Item("3.3"));
this.lb1.ItemsSource = Items;
this.tv1.ItemsSource = Items;
}
}
}
Item.cs
namespace TestWpf
{
public class Item
{
public List<Item> Items { get; set; }
public string Caption { get; set; }
public Item(string caption)
{
this.Caption = caption;
this.Items = new List<Item>();
}
}
}
I just copy and pasted your code in VS, pressed F5 and it worked.
The only thing i had to change was the namespace since i created it with WPFApplication1 :)
Try taking your code out to a fresh project and try to build it and see if it works.
I'm self-learning C#, OOP, and WPF so the potential for stuff ups is staggering.
So given that, can someone please explain why after clicking the button in my tiny test example the Name property appears in the TextBox but the ListBox shows nothing?
XAML:
<Window x:Class="BindingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingTest" Height="250" Width="300">
<Grid Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Name="MakeIntListButton"
Click="MakeIntListButton_Click">Make and Display Integer List</Button>
<TextBox Grid.Row="1" Text ="{Binding Path=Name}"
/>
<ListBox
Grid.Row="2"
ItemsSource="{Binding Path=MyIntegers}"
/>
</Grid>
C#:
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.Navigation;
using System.Windows.Shapes;
namespace BindingTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void MakeIntListButton_Click(object sender, RoutedEventArgs e)
{
AClass InstanceOfAClass = new AClass();
InstanceOfAClass.MyIntegers.Add(6);
InstanceOfAClass.MyIntegers.Add(7);
InstanceOfAClass.MyIntegers.Add(42);
InstanceOfAClass.Name = "Fred";
mainGrid.DataContext =InstanceOfAClass ;
}
}
public class AClass
{
public string Name {get;set;}
public List<int> MyIntegers = new List<int>();
}
}
Part of me wonders whether it's something to do with the fact that "MyIntegers" is a public field rather than a property. Can you refactor you class to look like this and try it?
public class AClass
{
private List<int> _ints = new List<int>();
public string Name { get; set; }
public List<int> MyIntegers
{
get { return _ints; }
}
}
I ran your sample and when I clicked on the button the TextBox was populated with the Name as expected.
The only problem I encountered was that the ListView was not getting populated with the list of integers. That's to do with the fact that XAML is not very comfortable with generics if you modify it to bind to an array instead it works. WPF supports consumption of XAML fine, it's using generics within XAML that's not supported. As Matt Hamilton points out in his answer MyIntegers just needs to be made a propety by adding a get acessor.
Add C# Property:
public int[] MyInts { get { return MyIntegers.ToArray(); } }
XAML:
<ListBox Grid.Row="2" ItemsSource="{Binding Path=MyInts}" />
Look into using the System.Collections.ObjectModel.ObservableCollection for list binding instead of a plain List.