Using an object in code, which is instantiated in xaml - c#

I am trying to experiment making simple GUI application.
In it I have made a simple class called consle. I am using a button click to trigger an event Button_Click_1. This should in turn call the function wrStr which changes the value of variable change internally. This changed value should reflect in the GUI because the variable change is also bound in the XAML code.
The problem is, I do not know how to access the consle type object instantiated in XAML code. If I get the name of the object instantiated in XAML I could just say <NameOfObject>.wrStr() inside the Button_Click_1 function.
The XAML code is given below:
<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">
<Grid>
<Button Content="Set44" HorizontalAlignment="Left" Margin="244,37,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
<TextBlock HorizontalAlignment="Left" Margin="339,76,0,0" TextWrapping="Wrap" Text="{Binding Source=consle, Path=change, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top"/>
</Grid>
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();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
}
}
public class consle
{
public string mainstr {get; set;}
public int change { get; set; }
public consle()
{
}
public void wrStr()
{
change = 44;
}
}
}

Related

why does the datacontext in my WPF project function correctly from code behind but not from xaml? [duplicate]

This question already has answers here:
WPF Binding Image Source
(2 answers)
Closed 5 years ago.
I have been working on a small sample Wpf Mvvm project for experimenting with INotifyPropertyChanged interface. The project actually works correctly, but the problem that I am having is that the project only works correctly if I set the DataContext in the code behind of MainWindow.xaml. If I try to set the DataContext in the xaml markup then some of the features of the project don't work. The UI contains a textblock, textbox (for entering text to display in the textblock OnPropertyChanged) and submit button (which really does nothing except provide a place to lose focus from textbox) and 3 other buttons (color buttons) for changing the background color of the UI. The default color of the UI is orange -- until the color is changed by clicking any of the color buttons
There are 3 viewModels, PersonViewModel (which the textbox binds to), BackgroundViewModel (for the color buttons) and a MainViewModel which combines the two other viewModels. The viewModels reside in the viewModels folder of the project. There is also an ObservableObject class (ViewModelBase class basically) which implement INotifyPropertyChanged interface and gets inherited by PersonViewModel and BackgroundViewModel. ObservableObject.cs resides in the root folder of the project.
The Project isn't pure Mvvm. The color buttons use a click event in the code behind of MainWindow.xaml. If I set the DataContext in the Code behind of MainWindow.xaml everything works correctly. If I set the DataContext in the xaml markup -- the textbox/textblock features works but the color buttons won't change the background color of the UI. When I step through the code it runs through all the code correctly but the UI background colors don't change. I am guessing it is a binding thing.
The sample project can be downloaded here
The code is below. How can I make this project function correctly if I set the DataContext in the xaml markup? I tried the following binding on the Grid which WILL set the default orange color for the UI, but the color buttons don't work:
<Grid Background="{Binding Background.Color}" DataContext="{StaticResource bc}">
--MainWindow.xaml
<Window x:Class="NotifyChangeExample.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:NotifyChangeExample"
xmlns:VM="clr-namespace:NotifyChangeExample.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="550" Width="525">
<!--<Window.DataContext>
<VM:MainViewModel />
</Window.DataContext>-->
<Window.Resources>
<VM:MainViewModel x:Key="bc" />
</Window.Resources>
<Grid Background="{Binding Background.Color}" DataContext="{StaticResource bc}">
<!--<Grid Background="{Binding Background.Color}">-->
<DockPanel LastChildFill="False" Margin="0,82,0,0">
<StackPanel Width="150" DockPanel.Dock="Top">
<TextBlock Text="{Binding Person.Name, StringFormat=Welcome (0)}" />
<TextBox Text="{Binding Person.Name, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
<Button>Submit</Button>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" DockPanel.Dock="Bottom" >
<Button Click="Red_Clicked">Red Background</Button>
<Button Click="Blue_Clicked">Blue Background</Button>
<Button Click="Yellow_Clicked">Yellow Background</Button>
</StackPanel>
</DockPanel>
</Grid>
</Window>
--MainWindow.xaml.cs
using NotifyChangeExample.ViewModels;
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 NotifyChangeExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
//DataContext = _main;
}
private void Red_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Red);
}
private void Blue_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Blue);
}
private void Yellow_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Yellow);
}
}
}
--ObservableObject.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyChangeExample
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
--PersonViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyChangeExample.ViewModels
{
public class PersonViewModel : ObservableObject
{
private string _name;
public string Name
{
get
{
if (string.IsNullOrEmpty(_name))
return "Unknown";
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
}
}
--BackgroundViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace NotifyChangeExample.ViewModels
{
public class BackgroundViewModel : ObservableObject
{
private Brush _color;
public Brush Color
{
get
{
if (_color == null)
return Brushes.Orange;
return _color;
}
set
{
_color = value;
OnPropertyChanged("Color");
}
}
}
}
--MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace NotifyChangeExample.ViewModels
{
public class MainViewModel
{
public PersonViewModel Person { get; private set; }
public BackgroundViewModel Background { get; private set; }
public MainViewModel()
{
Person = new PersonViewModel();
Background = new BackgroundViewModel();
}
public void SetBackground(Brush brushColor)
{
Background.Color = brushColor;
}
}
}
Your code behind is using the _main object so if you want to set the DataContext in the XAML, you just need to set _main using the DataContext.
So in the XAML you would have
<Window.DataContext>
<VM:MainViewModel />
</Window.DataContext>
and in your code behind you would set _main by casting the DataContext to a MainViewModel
MainViewModel _main;
public MainWindow()
{
InitializeComponent();
_main = (MainViewModel) DataContext;
}
Alternatively, remove the DataContext from XAML, and use this MainWindow constructor:
private readonly MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _main;
}
When you are binding your ViewModel from XAML it can't work, because in your code-behind you are setting the colors to your local ViewModel "_main". But _main is not bound to the View, bc is.

Editable combobox overwrites my context menu - Fails

I have been trying to use the code in the reply to Question
"Editable combobox overwrites my context menu" asked on asked Aug 30 '12 at 14:24
But it always errors with a nullreferenceerror on
(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(combobox, 0), 2) as TextBox).ContextMenu = combobox.ContextMenu;
In order to minimize any environment problems I have added the code as a new project
<Window x:Class="WpfApplication1.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>
<ComboBox IsEditable="True" ContextMenuService.ShowOnDisabled="True"
Name="combobox" Loaded="combobox_Loaded">
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="test"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>
<ComboBoxItem Content="Item1"></ComboBoxItem>
<ComboBoxItem Content="Item2"></ComboBoxItem>
</ComboBox>
</Grid>
</Window>
code
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
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void combobox_Loaded(object sender, RoutedEventArgs e)
{
(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(combobox, 0), 2) as TextBox).ContextMenu = combobox.ContextMenu;
}
}
}
This is working:
private void combobox_Loaded(object sender, RoutedEventArgs e)
{
(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(combobox, 0), 2), 0) as TextBox).ContextMenu = combobox.ContextMenu;
}
You have to go another level.
Because the TextBox is inside a border.
You can use Snoop application to inspect the xaml tree.

The name "X" does not exist in the namespace "clr-namespace:Y"

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.

List<string> is not updated back to ListBox's ItemsSource?

I'm new to WPF. I have a List<string> as a source for my ListBox's ItemsSource. Initially, the ListBox shows all the Items in my List<string> OK. However, after trying adding some string to my List<string>, the ListBox doesn't update the changes. I'm using Binding to bind the data (behind) with the ListBox (view), here is my code:
//Code behind
public MainWindow: Window {
public MainWindow(){
InitializeComponent();
Items = new List<string>(){"1","2","3"};//after loaded, all these values are displayed OK in my ListBox.
DataContext = this;
//Try clicking on a button to add new value
button1.Click += (s,e) => {
Items.Add("4");//But my ListBox stays the same without any update/changes.
};
}
public List<string> Items {get;set;}
}
//XAML
<ListBox ItemsSource={Binding Items}/>
Could you please point out what I'm doing wrong here and give me a solution? Thank you very much in advance.
If you had read the documentation of ItemsSource you would already know what is wrong.
[...]
This example shows how to create and bind to a collection that derives from the ObservableCollection<T> class, which is a collection class that provides notifications when items get added or removed.
you should try ObservableCollection instead because it's
Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
<Window x:Class="WpfApplication3.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>
<Button Click="Button_Click" Content="Button" HorizontalAlignment="Left" Margin="441,289,0,0" VerticalAlignment="Top" Width="75"/>
<ListBox HorizontalAlignment="Left" ItemsSource="{Binding MyList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="lstbox" Height="296" Margin="21,23,0,0" VerticalAlignment="Top" Width="209"/>
</Grid>
</Window>
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 WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> _myList = new ObservableCollection<string>(new List<string>(){"1","2","3"});
int i = 3;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MyList.Add(i++.ToString());
}
public ObservableCollection<string> MyList
{
get { return _myList; }
set { _myList = value; }
}
}
}

How do I bind to a List<T> using DataContext?

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.

Categories

Resources