Data binding to the output of a method - c#

I'm trying to display the output of a method in a WPF TextBox. I'm just trying a simple attempt, to print a single string 3 in a TextBox.
I'm trying to do it the following way, using an ObjectDataProvider:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="dataprovider" ObjectType="{x:Type system:String}" MethodName="GetValue">
</ObjectDataProvider>
</Window.Resources>
<Grid>
<TextBox Text="{Binding Source={StaticResource dataprovider}, Mode=OneWay}" HorizontalAlignment="Left" Height="23" Margin="201,168,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
</Grid>
</Window>
And here's my code behind:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string GetValue()
{
return "3";
}
}
}
I'm getting no output. the TextBox is just blank. Where am I going wrong?

Instead of ObjectDataProvider create a property like this:
public string GetMethod
{
get
{
return GetValue();
}
}
And:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Then in the XAML remove ObjectDataProvider and just:
<TextBox Text="{Binding GetMethod, Mode=OneWay}" HorizontalAlignment="Left"
Height="23" Margin="201,168,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>

Related

ContentControl not refreshing View when new ViewModel is created

I am trying to make an app using using MVVMLight that has a sidebar with buttons, each button shows a new usercontrol hosted in a ContentControl. When the user clicks the buttons in the sidebar (Show View1 / Show View2) it shows the views correctly.
My problems arise when the user has already shown a View (it could be View1 or View2 ), say View1 and click (Show View1) ** again ** View1 controls remain with the same values and I would like to show the same view with all controls as if it had restarted. (I know that I could reset all controls within the user control (View1) but my application requires having a new instance of the view every time the button is clicked).
Somehow, in the same scenario when the user is in View 1, switch to view 2 and return to view 1, a new instance is created and shown as I would like. I would like to get the same result without the need to switch to view 2 and return.
Here is a minimal reproducible example of the issue:
ViewModelLocator.cs
using CommonServiceLocator;
using GalaSoft.MvvmLight.Ioc;
namespace SidebarApp.ViewModel
{
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Register viewmodels
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<View1ViewModel>();
SimpleIoc.Default.Register<View2ViewModel>();
}
public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(); } }
public View1ViewModel View1Vm { get { return ServiceLocator.Current.GetInstance<View1ViewModel>(); } }
public View2ViewModel View2Vm { get { return ServiceLocator.Current.GetInstance<View2ViewModel>(); } }
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
MainViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows.Input;
namespace SidebarApp.ViewModel
{
public class MainViewModel : ViewModelBase
{
// Commands
public ICommand ShowView1Command { get; private set; }
public ICommand ShowView2Command { get; private set; }
/// <summary>
/// Property that will cointain the current viewmodel to show
/// ViewModelBase inplements ObservableObject class which imlements INotifyPropertyChanged
/// </summary>
public ViewModelBase CurrentViewModel { get; set; }
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
this.ShowView1Command = new RelayCommand(ShowView1);
this.ShowView2Command = new RelayCommand(ShowView2);
}
private void ShowView1()
{
// I tried this but it doesn't work
//CurrentViewModel = null or View2ViewModel;
CurrentViewModel = new View1ViewModel();
}
private void ShowView2()
{
CurrentViewModel = new View2ViewModel();
}
}
}
MainWindow.xaml
<Window x:Class="SidebarApp.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:SidebarApp"
mc:Ignorable="d"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="MainWindow" Height="348.965" Width="560.683">
<Window.Resources>
<DataTemplate DataType="{x:Type local:View1ViewModel}">
<local:View1View />
</DataTemplate>
<DataTemplate DataType="{x:Type local:View2ViewModel}">
<local:View2View />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel>
<Button Command="{Binding ShowView1Command}">Show View1</Button>
<Button Command="{Binding ShowView2Command}">Show View2</Button>
</StackPanel>
<ContentControl Grid.Column="1" Content="{Binding Path=CurrentViewModel}"></ContentControl>
</Grid>
</Window>
View1View.xaml
<UserControl x:Class="SidebarApp.View1View"
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:SidebarApp"
mc:Ignorable="d"
DataContext="{Binding View1Vm, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="LightPink">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
<TextBox HorizontalAlignment="Stretch" Text="Original text value"/>
<CheckBox Content="Some boolean value"/>
</StackPanel>
</Grid>
</UserControl>
View1ViewModel.cs
using GalaSoft.MvvmLight;
namespace SidebarApp
{
public class View1ViewModel : ViewModelBase
{
public View1ViewModel()
{
}
}
}
View2View.xaml
<UserControl x:Class="SidebarApp.View2View"
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:SidebarApp"
mc:Ignorable="d"
DataContext="{Binding View2Vm, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="LightCyan">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Welcome to View 2" FontSize="20"/>
<TextBox HorizontalAlignment="Stretch" Text="Some text in view2"/>
<Button Content="Button"/>
</StackPanel>
</Grid>
</UserControl>
View2ViewModel.cs
using GalaSoft.MvvmLight;
namespace SidebarApp
{
public class View2ViewModel : ViewModelBase
{
public View2ViewModel()
{
}
}
}
App.xaml
<Application x:Class="SidebarApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SidebarApp" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:SidebarApp.ViewModel" />
</ResourceDictionary>
</Application.Resources>
</Application>
For example I enter to the view1 and change the value of the textbox ("Original text value") to another value and I click again in Show View1 the text and everything in the usercontrol stays the same but when I switch to view2 and go back to view1 a new usercontrol is shown with his original values.
This might feel like something of a workaround, but it works. Hopefully it will prove useful. (Obviously make same changes to View2View view/view model).
ViewModelLocator.cs
public View1ViewModel View1Vm { get { return new View1ViewModel(); } }
MainViewViewModel.cs
private void ShowView1()
{
CurrentViewModel = new ViewModelLocator().View1Vm;
}
View1View.xaml
<UserControl x:Class="SidebarApp.View1View"
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:SidebarApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="LightPink">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
<TextBox HorizontalAlignment="Stretch" Text="{Binding Path=View1Text, Mode=TwoWay}"/>
<CheckBox Content="Some boolean value" IsChecked="{Binding Path=CheckBoxChecked, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</UserControl>
View1ViewModel.cs
public class View1ViewModel : ViewModelBase
{
private string _view1Text;
private bool _checkBoxedChecked;
public string View1Text
{
get
{
return _view1Text;
}
set
{
_view1Text = value;
RaisePropertyChanged("View1Text");
}
}
public bool CheckBoxChecked
{
get
{
return _checkBoxedChecked;
}
set
{
_checkBoxedChecked = value;
RaisePropertyChanged("CheckBoxChecked");
}
}
public View1ViewModel()
{
View1Text = "Original text value";
}
}

WPF update textblock from different window

I have a window called RolledPaper.xaml with a textblock called SequenceValue.
SequenceValue is defined in another window called CounterSettings.xaml by typing in a textbox called SequenceRequested. I would like to have SequenceValue be always in sinch with SequenceRequested.
I tryed and failed using the same datacontext for both windows.
here it is the code for RolledPaper.xaml:
<Window x:Class="Numbering3.View.RolledPaper"
WindowStyle="None"
ResizeMode="NoResize"
BorderBrush="LightGray"
BorderThickness="5"
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:Numbering3.View"
xmlns:vm="clr-namespace:Numbering3.ViewModel"
xmlns:cv="clr-namespace:Numbering3.Helper"
xmlns:vo="clr-namespace:Numbering3.ViewModel"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="RolledPaper" Height="750" Width="1218"
Background="{DynamicResource WindowBrush}"
DataContextChanged="Rolledpaper_DataContextChanged">
<Window.DataContext>
<vm:ViewModelRolledPaper/>
</Window.DataContext>
<TextBlock x:Name="SequenceValue" Grid.Column="3" HorizontalAlignment="Left" Height="19" Margin="72,310,0,0" TextWrapping="Wrap"
Background="White" VerticalAlignment="Top" Width="98" Text="{Binding _SequenceValueToShow.SequenceValuetoShow, Mode=TwoWay, FallbackValue=NNN, TargetNullValue=NNN, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
code behind:
public partial class RolledPaper : Window
{
public RolledPaper()
{
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
SaveKeeper.fromMain = false;
InitializeComponent();
this.MouseLeftButtonDown += delegate { this.DragMove(); };
DataContextChanged += new DependencyPropertyChangedEventHandler(Rolledpaper_DataContextChanged);
}
the CounterSetting window:
<Window x:Class="Numbering3.View.CounterSettings"
...
...
<Window.DataContext>
<ViewModelCounterSettings/>
</Window.DataContext>
<Grid Margin="0,-19,-0.4,0">
<TextBox x:Name="SequenceRequested" PreviewTextInput="SequenceValidationTextBox" MaxLength="10" HorizontalAlignment="Left" Height="23" Margin="154,324,0,0" TextWrapping="Wrap"
Text="{Binding Path=_SequenceValueToShow.SequenceValueToKeep, FallbackValue='NNN', TargetNullValue ='NNN', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }" VerticalAlignment="Top" Width="120" TextChanged="SequenceRequested_TextChanged" />
</Grid>
its code behind:
public partial class CounterSettings : Window
{
public CounterSettings()
{
InitializeComponent();
this.MouseLeftButtonDown += delegate { this.DragMove(); };
DataContextChanged += new DependencyPropertyChangedEventHandler(CounterSettings_DataContextChanged);
}
And the SequeneValue class:
public class SequenceValue : INotifyPropertyChanged
{
public string SequenceValueToKeep
{
get
{
return _sequenceValueToKeep=_sequenceProcessor.GetSequence();
}
set
{
if (_sequenceValueToKeep != value)
{
__sequenceValueToKeep = value;
RaisePropertyChanged("SequenceValueToKeep");
}
}
}
private void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I need to put a value in the textbox of countersetting =>string Sequencevalue.SequenceValueToKeep is updated => textblock in RolledPaper window shows Sequencevalue.SequenceValueToKeep .
Thank you.
Solutio 1:
You can use one Static ViewModel to get the to to know each other.
in App.xaml
<Application.Resources>
...
<MainViewModel x:Key="mainViewModel" />
</Application.Resources>
and then in CounterSetting.xaml
<Window otherProperties=...
DataContext="{Binding ViewModelCounterSettings, Source={StaticResource mainViewModel}}"/>
and in RolledPaper.xaml
<Window otherProperties=...
DataContext="{Binding ViewModelRolledPaper, Source={StaticResource mainViewModel}}"/>
The MainViewModel does inits your two ViewModel and makes them accessible as properties and also gives the the same instance of SequenceValue
Solution 2:
Create an object of SequenceValue as an static resource, like
<Application.Resources>
...
<SequenceValue x:Key="sequenceValue"/>
</Application.Resources>
and set it as the DataContext of your TextBox and TextBlock
<TextBox DataContext="{StaticResource sequenceValue}"
Text="{Binding SequenceValueToKeep, FallbackValue='NNN', TargetNullValue='NNN', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }"
... />
<TextBlock DataContext="{StaticResource sequenceValue}"
Text="{Binding SequenceValuetoShow, FallbackValue=NNN, TargetNullValue=NNN, UpdateSourceTrigger=PropertyChanged}"

How to change elements for the child elements inside a user control. - In the designer

Well I made my custom usercontrol which is "simply" a combination of a label and a textbox.
<UserControl x:Class="testit.LabelTextBox"
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="30" d:DesignWidth="300">
<DockPanel LastChildFill="False">
<TextBox x:Name="textBox" TextWrapping="Wrap" DockPanel.Dock="Right" VerticalAlignment="Center" Text="{Binding TextBoxText}" />
<Label x:Name="label" DockPanel.Dock="Right" Target="{x:Reference textBox}" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Label Text"/>
</DockPanel>
</UserControl>
I then created a usercontrol class with the dependecy property:
using System.Windows;
using System.Windows.Controls;
namespace simtest
{
/// <summary>
/// Interaction logic for LabelTextBox.xaml
/// </summary>
public partial class LabelTextBox : UserControl
{
public string TextBoxText {
get { return (string)GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
public readonly static DependencyProperty TextBoxTextProperty =
DependencyProperty.Register("TextBoxTextProperty",
typeof(string), typeof(LabelTextBox), new UIPropertyMetadata(null));
public LabelTextBox() {
InitializeComponent();
DataContext = this;
}
}
}
Now this "works" as in that it compiles. And if I use above control as:
<local:LabelTextBox TextBoxText="foobar"></local:LabelTextBox>
It will show correctly - while running -.
However I need this for each and every element of the textbox I wish to be able to adapt - height width etc etc. This isn't really ideal: can I make it so that I can just edit the LabelTextBox' TextBox directly?

Binding with a single Window Class

This is out of curiousity question. I know you should not structure any real WPF applications this way.
Working within and using only the MainWindow Class how do you bind an XAML element to a CLR property?
Here is my XAML.
<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>
<TextBlock DockPanel.Dock ="Top" Height="50" Width="50"
Background ="AliceBlue" FontSize ="16" />
</Grid>
</Window>
And Code
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
string _myString = "hello world";
public string MyString
{
get { return _myString; }
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
}
So I want to display the string in the textblock. I think its a simple binding but I can't find the right syntax. Thanks
You must set your window data context to itself.
<Window.DataContext>
<Binding RelativeSource="{RelativeSource Self}"/>
</Window.DataContext>
then you cant bind to your property MyString
<TextBlock DockPanel.Dock ="Top" Height="50" Width="50"
Background ="AliceBlue" FontSize ="16" />
In this case you can simply do:
<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"
x:Name="MainWindow">
<Grid>
<TextBlock DockPanel.Dock ="Top" Height="50" Width="50"
Background ="AliceBlue" FontSize ="16"
Text="{Binding MyString, ElementName=MainWindow}" />
</Grid>
</Window>

Databinding XmlDocument

I have a UserControl where I want to bind textboxes to an XmlDocument.
The important parts of the xaml-code looks like:
...
<UserControl.DataContext>
<XmlDataProvider x:Name="Data" XPath="employee"/>
</UserControl.DataContext>
...
<TextBox Text={Binding XPath=general/description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
...
In the constructor of the usercontrol I have the following lines:
string xmlPath = System.IO.Path.Combine(Thread.GetDomain().BaseDirectory, "Data", "TestXml.xml");
FileStream stream = new FileStream(xmlPath, FileMode.Open);
this.Data.Document = new XmlDocument();
this.Data.Document.Load(stream);
If I changed textbox-text, the XmlDocument Data is not updated. What do I have to do, to achieve this?
The above code worked for me. Instead of using a stream, I used a hard-coded data.
XAML File :
<Window x:Class="TestWPFApp.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">
<Window.DataContext>
<XmlDataProvider x:Name="Data" XPath="employee"/>
</Window.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<TextBox Width="100" Foreground="Red" Height="20" Text="{Binding XPath=general/description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Test" Width="50" Height="20" Click="Button_Click"></Button>
</StackPanel>
</Grid>
</Window>
Code Behind :
using System.Windows;
using System.Xml;
namespace TestWPFApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Data.Document = new XmlDocument();
this.Data.Document.LoadXml(#"<employee><general><description>Test Description</description></general></employee>");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var data = this.Data.Document.SelectSingleNode("descendant::description").InnerText;
}
}
}

Categories

Resources