Databinding XmlDocument - c#

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;
}
}
}

Related

String that has an xaml element form from C# to XAML code

I am trying to pass an textBlock that contains a hyperlink. Something like this:
string textblock = "<TextBlock>Hello<Hyperlink NavigateUri="https://google.com"RequestNavigate="Hyperlink_RequestNavigate">Click</Hyperlink></TextBlock>"
i want to pass that string into the grid without using instances of objects. i read smth about xaml reader, loader but i cant figure it out...
my xaml looks like this:
<Window x:Class="DailyText.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"
Title="any" Height="450" Width="800" >
<Grid>
</Grid>
</Window>
Try to see if the code below fits your needs.
Xaml:
<Grid x:Name="grid">
<TextBlock Name="TextBlockWithHyperlink" Background="Orange" Width="100" Height="50" HorizontalAlignment="Left">
Hello
<Hyperlink NavigateUri="https://google.com" RequestNavigate="Hyperlink_RequestNavigate">
Click
</Hyperlink>
</TextBlock>
</Grid>
xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TextBlock tb = new TextBlock();
Hyperlink link=new Hyperlink() { NavigateUri=new Uri("https://google.com") };
link.Inlines.Add("click");
link.RequestNavigate+= Hyperlink_RequestNavigate;
tb.Inlines.Add("Hello");
tb.Inlines.Add(link);
tb.Height=60;
tb.Background=Brushes.LightSeaGreen;
tb.HorizontalAlignment= HorizontalAlignment.Center;
grid.Children.Add(tb);
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(e.Uri.ToString());
}
}

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: ContentTemplate of ContentControl is always null

I have a ContentControl bound to an instance of class X and a DataTemplate for X in the resource section of the Main Window.
<Window x:Class="DT1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DT1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:X}">
<TextBlock Text="Hello, World!"/>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="Press me" Click="Button_Click" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ContentControl x:Name="cont" Background="Pink" Content="{Binding MyX}" Tag="Blobby"/>
</StackPanel>
</Grid>
</Window>
In the button click handler in the MainWindow code behind I have a handler which tries to access the ContentTemplate of the ContentControl but this is always null:
using System.Windows;
namespace DT1
{
public class X
{
}
public partial class MainWindow : Window
{
public X MyX { get; set; } = new X();
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var template = cont.ContentTemplate;
System.Diagnostics.Debug.WriteLine($"ContentTemplate: {template?.ToString() ?? "template is null"}");
}
}
}
What is the right way to access the visual items in the DataTemplate for X?
It is null because you are not setting any datatemplate explicitly. When you define a datatemplate without providing an x:key and providing a DataType using x:Type you are making use of what it is known as Implicit Datatemplates.
In order to get the datatemplate from codebehind you must assign it explicitly:
<Window.Resources>
<DataTemplate DataType="{x:Type local:X}" x:Key="XTemplate">
<TextBlock Text="Hello, World!"/>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="Press me" Click="Button_Click" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ContentControl x:Name="cont" Background="Pink" Content="{Binding MyX}" Tag="Blobby" ContentTemplate="{StaticResource XTemplate}"/>
</StackPanel>
</Grid>
Hope this helps!

Data binding to the output of a method

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"/>

What is the best way to reuse blocks of XAML?

I've got many user controls like this:
PageManageCustomers.xaml.cs:
public partial class PageManageCustomers : BasePage
{
...
}
which inherit from:
PageBase.cs:
public class BasePage : UserControl, INotifyPropertyChanged
{
...
}
Since PageBase.cs has no accompanying XAML file, I have to put the XAML that it refers to in each of the user controls which inherit it, e.g. the following block is repeated in every XAML file of every control that inherits PageBase:
<DataTemplate x:Key="manageAreaCellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"/>
<TextBlock Text=" "/>
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"/>
</StackPanel>
</DataTemplate>
I'm trying to put this block into a resource file but can't get the syntax right, it says:
'ResourceDictionary' root element
requires a x:Class attribute to
support event handlers in the XAML
file. Either remove the event handler
for the MouseDown event, or add a
x:Class attribute to the root element.
Or perhaps I could read these blocks in with XamlReader` somehow?
How can I put this repeated block of code in one place so that it is not repeated in every XAML file that inherits BagePage?
Here is a reproducable example of this problem:
Window1.xaml:
<Window x:Class="TestXamlPage8283.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">
<StackPanel x:Name="MainContent"/>
</Window>
Window1.xaml.cs:
using System.Windows;
namespace TestXamlPage8283
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Page1 page1 = new Page1();
MainContent.Children.Add(page1);
Page2 page2 = new Page2();
MainContent.Children.Add(page2);
}
}
}
Page1.xaml:
<local:BasePage x:Class="TestXamlPage8283.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestXamlPage8283"
Height="40" Width="300">
<StackPanel>
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
<TextBlock Text="This is XAML that is specific to page one." />
</StackPanel>
</local:BasePage>
Page1.xaml.cs:
namespace TestXamlPage8283
{
public partial class Page1 : BasePage
{
public Page1()
{
InitializeComponent();
PageTitle = "Page One";
}
}
}
Page2.xaml:
<local:BasePage x:Class="TestXamlPage8283.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestXamlPage8283"
Height="40" Width="300">
<StackPanel>
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
<TextBlock Text="This is XAML that is specific to page two." />
</StackPanel>
</local:BasePage>
Page2.xaml.cs:
namespace TestXamlPage8283
{
public partial class Page2 : BasePage
{
public Page2()
{
InitializeComponent();
PageTitle = "Page Two";
}
}
}
BasePage.cs:
using System.Windows.Controls;
using System.ComponentModel;
namespace TestXamlPage8283
{
public class BasePage : UserControl, INotifyPropertyChanged
{
#region ViewModelProperty: PageTitle
private string _pageTitle;
public string PageTitle
{
get
{
return _pageTitle;
}
set
{
_pageTitle = value;
OnPropertyChanged("PageTitle");
}
}
#endregion
public BasePage()
{
DataContext = this;
}
#region INotifiedProperty Block
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
How do I take this block
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
out of Page1.xaml and Page2.xaml and put it in one place so I can refer to it from Page1.xaml and Page2.xaml? (so that when I want to change FontSize=14 to FontSize=16, I just have to change it in one place)
Use resource dictionaries - add a MyDictionary.xaml file to your project, setting its Build Action to "Page":
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="manageAreaCellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"/>
<TextBlock Text=" "/>
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
Then in other XAML files you refer to it with:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
... some other local resources ...
</ResourceDictionary>
and refer to your resource as Template={StaticResource manageAreaCellTemplate}.

Categories

Resources