This is a migrated thread and some comments may be shown as answers.

XML Data and Two Way Binding

3 Answers 442 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Garry
Top achievements
Rank 1
Garry asked on 08 Mar 2010, 10:24 PM
Hello all,
I am trying to create a configuration window that binds to an external XML file and implements two way binding in XAML. I have basically used the RadGridView demo as a starting point and have started to modify my code to implement the the two way binding piece, however I have hit a road block.

First off I am wondering whether I should use a DependencyObject or implement INotifiedProperty. At this point I am using a DependencyObject, however my textboxes do not appear to bind to the data, but the RadGridView does.

Basically I want to use the RadGridView as a list box on steroids and use the textboxes to edit/add records. However since this is an external xml file I am bit confused on what direction to take. Also I am using a ObservableCollection as my datasource, just as you do in the RadGridView demo, but I have also played around with using a XMLDataProvider. Any suggestions here?

Below are the basic classes I have so far.

Thanks for your help!

XAML
<telerikNavigation:RadWindow x:Class="VistaAdmin.Configuration.Presentation.ConfigurationView" 
    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:telerikSchema="http://schemas.telerik.com/2008/xaml/presentation" 
    xmlns:v="clr-namespace:VistaAdmin.Configuration.Presentation" 
    xmlns:p="clr-namespace:VistaAdmin.Configuration.Properties" 
    xmlns:db="clr-namespace:VistaAdmin.Configuration.Models" 
    xmlns:telerikGrid="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView" 
    xmlns:telerikNavigation="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation" 
    xmlns:telerikRibbonBar="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.RibbonBar" 
    xmlns:telerikInput="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input" 
    xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" 
    xmlns:xamlHelpers="clr-namespace:VistaAdmin.Configuration.Domain" 
    Header="{Binding HeaderInfo}"   
    mc:Ignorable="d" Height="420" Width="695" WindowStartupLocation="CenterOwner"   
    FontFamily="Calbri" ResizeMode="NoResize" telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}">  
    <telerikNavigation:RadWindow.Resources> 
        <!--<XmlDataProvider Source="DataSources\Environments.xml" x:Key="xmlData" XPath="/EnvironmentList" />--> 
        <db:XmlDataSource x:Key="xmlData" Source="Environments.xml"/>  
    </telerikNavigation:RadWindow.Resources> 
    <StackPanel>         
        <telerikNavigation:RadToolBar telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}">  
                <Button x:Name="configSaveButton" ToolTip="{x:Static p:Resources.Save}" Command="{Binding SaveConfigCommand}">  
                    <Image Source="../Resources/save.png" Width="16" Height="16"/>  
                </Button> 
                <Button x:Name="configValidateButton" ToolTip="{x:Static p:Resources.Validate}" Command="{Binding DBValidateCommand}">  
                    <Image Source="../Resources/db.png" Width="16" Height="16"/>  
                </Button> 
            </telerikNavigation:RadToolBar> 
        <Grid Height="Auto" > 
            <Grid.ColumnDefinitions> 
                <ColumnDefinition Width="250" MinWidth="180" /> 
                <ColumnDefinition Width="Auto" MinWidth="40" /> 
                <ColumnDefinition Width="*" MinWidth="180"/>  
            </Grid.ColumnDefinitions> 
            <Grid Grid.Column="0">  
                <Grid.RowDefinitions> 
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                    <RowDefinition MinHeight="25"/>  
                </Grid.RowDefinitions> 
                <Label x:Name="labelName" Grid.Row="0" Margin="5,0,5,0">  
                    <TextBlock Text="Environment Name:" TextWrapping="Wrap" Foreground="Black" /> 
                </Label> 
                <TextBox Grid.Row="1" x:Name="textName" Margin="5,0,5,0" Text="{Binding Path=Name, Mode=TwoWay}" /> 
                <Label x:Name="labelServerName" Grid.Row="2" Margin="5,0,5,0">  
                    <TextBlock Text="DB Server Name:" TextWrapping="Wrap" Foreground="Black" /> 
                </Label> 
                <TextBox Grid.Row="3" x:Name="textServerName" Margin="5,0,5,0" Text="{Binding Path=Servername, Mode=TwoWay}"/>  
                <Label x:Name="labelUserName" Grid.Row="4" Margin="5,0,5,0">  
                    <TextBlock Text="User Name:" TextWrapping="Wrap" Foreground="Black" /> 
                </Label> 
                <TextBox Grid.Row="5" x:Name="textUserName" Margin="5,0,5,0" Text="{Binding Path=Username, Mode=TwoWay}"/>  
                <Label x:Name="labelPassword" Grid.Row="6" Margin="5,0,5,0">  
                    <TextBlock Text="Password:" TextWrapping="Wrap" HorizontalAlignment="Left" Foreground="Black" /> 
                </Label> 
                <PasswordBox Grid.Row="7" Margin="5,0,5,0" Name="textPassword" PasswordChar="*" 
                        xamlHelpers:PasswordBoxAssistant.BindPassword="true" 
                        xamlHelpers:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay}"/>  
                <Label x:Name="labelDBName" Grid.Row="8" Margin="5,0,5,0">  
                    <TextBlock Text="DB Name:" TextWrapping="Wrap" Foreground="Black" /> 
                </Label> 
                <TextBox Grid.Row="9" x:Name="textDBName" Margin="5,0,5,0" Text="{Binding Path=Databasename, Mode=TwoWay}"/>  
                <Label x:Name="labelServerType" Grid.Row="10" Margin="5,0,5,0">  
                    <TextBlock Text="DB Server Type:" TextWrapping="Wrap" Foreground="Black" /> 
                </Label> 
                <StackPanel Grid.Row="11" Orientation="Horizontal" Margin="5,0,5,0" HorizontalAlignment="Center" > 
                    <telerik:RadRadioButton Width="80" Height="22"  Margin="0 0 15 0" 
                                        HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsChecked="True"   
                                        telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}" > 
                        <telerik:RadRadioButton.Content> 
                            <StackPanel Orientation="Horizontal" Margin="4 0">  
                                <TextBlock Text="SQL" Foreground="#2b2b2d" FontSize="12"/>  
                            </StackPanel> 
                        </telerik:RadRadioButton.Content> 
                    </telerik:RadRadioButton> 
                    <telerik:RadRadioButton Width="80" Height="22" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" 
                                            telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}">  
                        <telerik:RadRadioButton.Content> 
                            <StackPanel Orientation="Horizontal" Margin="4 0">  
                                <TextBlock Text="Oracle"   Foreground="#2b2b2d" FontSize="12"/>  
                            </StackPanel> 
                        </telerik:RadRadioButton.Content> 
                    </telerik:RadRadioButton> 
                </StackPanel> 
                <Label x:Name="labelRootPath" Grid.Row="12" Margin="5,0,5,0">  
                    <TextBlock Text="Root Path:" TextWrapping="Wrap" Foreground="Black" /> 
                </Label> 
                <StackPanel Grid.Row="13" Margin="5,0,5,0" Orientation="Horizontal">  
                    <TextBox x:Name="textPath" HorizontalAlignment="Left" Margin="0,0,5,0" Width="210" Text="{Binding Path=RootPath, Mode=TwoWay}"/>  
                    <telerik:RadButton Width="25" HorizontalAlignment="Right" Command="{Binding BrowseCommand}" telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}">  
                        <telerik:RadButton.Content> 
                            <TextBlock Text="..." /> 
                        </telerik:RadButton.Content> 
                    </telerik:RadButton> 
                </StackPanel> 
            </Grid> 
            <Grid Grid.Column="1">  
                <StackPanel VerticalAlignment="Center" Margin="5,0,5,0">  
                    <telerik:RadButton Width="85" Height="22" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"   
                                       telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}" Command="{Binding AddCommand}">  
                        <telerik:RadButton.Content> 
                            <StackPanel Orientation="Horizontal" Margin="4,0,0,0" VerticalAlignment="Center">  
                                <Image Source="../Resources/Add.png" Width="16" Height="16" /> 
                                <TextBlock Text="Add" Foreground="#2b2b2d" FontSize="12" Margin="9 0 0 0"/>  
                            </StackPanel> 
                        </telerik:RadButton.Content> 
                    </telerik:RadButton> 
                    <telerik:RadButton Width="85" Height="22" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" Margin="0,5,0,0" Command="{Binding RemoveCommand}" 
                                       telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}">  
                        <telerik:RadButton.Content> 
                            <StackPanel Orientation="Horizontal" Margin="4,0,0,0" VerticalAlignment="Center">  
                                <Image Source="../Resources/Delete.png" Width="16" Height="16" /> 
                                <TextBlock Text="Remove" Foreground="#2b2b2d" FontSize="12" Margin="9 0"/>  
                            </StackPanel> 
                        </telerik:RadButton.Content> 
                    </telerik:RadButton> 
                </StackPanel> 
            </Grid> 
            <telerikGrid:RadGridView Grid.Column="2" Height="Auto" Name="gridEnvironments" Width="Auto" ItemsSource="{Binding Source={StaticResource xmlData}}" telerik:StyleManager.Theme="{Binding Source={StaticResource settings}, Path=Default.Theme}"/>  
        </Grid> 
    </StackPanel> 
</telerikNavigation:RadWindow> 
 

Dependency Object
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Xml;  
using System.Windows;  
using System.Xml.Serialization;  
 
namespace VistaAdmin.Configuration.Models  
{  
    public class Environment : DependencyObject    
    {
        #region DependencyProperties  
            public static readonly DependencyProperty ServernameProperty =   
                DependencyProperty.Register("Servername"typeof(String), typeof(Environment));  
            public static readonly DependencyProperty NameProperty =   
                DependencyProperty.Register("Name"typeof(String), typeof(Environment));  
            public static readonly DependencyProperty DatabasenameProperty =   
                DependencyProperty.Register("Databasename"typeof(String), typeof(Environment));  
            public static readonly DependencyProperty UsernameProperty =   
                DependencyProperty.Register("Username"typeof(String), typeof(Environment));  
            public static readonly DependencyProperty PasswordProperty =   
                DependencyProperty.Register("Password"typeof(String), typeof(Environment));  
            public static readonly DependencyProperty ServerTypeProperty =   
                DependencyProperty.Register("ServerType"typeof(String), typeof(Environment));  
            public static readonly DependencyProperty RootPathProperty =   
                DependencyProperty.Register("RootPath"typeof(String), typeof(Environment));
        #endregion  
 
        #region IEnvironment Members  
 
        [XmlAttribute(AttributeName = "Servername")]  
        public string Servername  
        {  
            get {return (string)GetValue(ServernameProperty);}  
            set {SetValue(ServernameProperty,value);}             
        }  
 
        [XmlAttribute(AttributeName = "Name")]          
        public string Name  
        {  
            get {return (string)GetValue(NameProperty);}  
            set {SetValue(NameProperty,value);}                   
        }  
 
        [XmlAttribute(AttributeName = "Databasename")]  
        public string Databasename  
        {  
             get {return (string)GetValue(DatabasenameProperty);}  
            set {SetValue(DatabasenameProperty,value);}      
        }  
 
        [XmlAttribute(AttributeName = "Username")]  
        public string Username  
        {  
             get {return (string)GetValue(UsernameProperty);}  
            set {SetValue(UsernameProperty,value);}        
        }  
 
        [XmlAttribute(AttributeName = "Password")]  
        public string Password  
        {  
             get {return (string)GetValue(PasswordProperty);}  
            set {SetValue(PasswordProperty,value);}        
        }  
 
        [XmlAttribute(AttributeName = "ServerType")]  
        public string ServerType  
        {  
             get {return (string)GetValue(ServerTypeProperty);}  
            set {SetValue(ServerTypeProperty,value);}        
        }  
 
        [XmlAttribute(AttributeName = "RootPath")]  
        public string RootPath  
        {  
             get {return (string)GetValue(RootPathProperty);}  
            set {SetValue(RootPathProperty,value);}        
        }
        #endregion         
    }  

Observable Collection
using System;  
using System.Collections.Generic;  
using System.Collections.ObjectModel;  
using System.Linq;  
using System.Text;  
using System.Xml;  
using System.Xml.Serialization;  
 
namespace VistaAdmin.Configuration.Models  
{  
    [XmlRoot(ElementName = "EnvironmentList")]  
    public class EnvironmentsList : ObservableCollection<Environment>  
    {  
        public void AddRange(IEnumerable<Environment> range)  
        {  
            foreach (Environment node in range)  
            {  
                this.Add(node);  
            }  
        }  
 
        //public void Add(Environment node)  
        //{  
        //        this.Add(node);  
        //}  
    }  

Datasource
using System;  
using System.Reflection;  
using System.Collections.Generic;  
using System.Collections.ObjectModel;  
using System.Linq;  
using System.Text;  
using System.Xml;  
using System.Xml.Serialization;  
using System.Xml.XPath;  
using System.IO;  
using System.Windows.Data;  
using System.ComponentModel;  
using Microsoft.Practices.EnterpriseLibrary.Logging;  
 
namespace VistaAdmin.Configuration.Models  
{  
    public class XmlDataSource : EnvironmentsList  
    {  
        private string source;  
          
        public string Source  
        {  
            get 
            {  
                return this.source;  
            }  
            set 
            {  
                try 
                {  
                    Assembly executingAssembly = Assembly.GetExecutingAssembly();  
                    string xmlFile = Path.GetDirectoryName(executingAssembly.Location) + @"\DataSources\" + value;  
                    this.source = xmlFile;  
 
                    AddRange(RetrieveData(File.Open(xmlFile, FileMode.Open)));  
                }  
                catch (Exception ex)  
                {  
                    Log("Error occured retrieving data:" + ex.Message, System.Diagnostics.TraceEventType.Error);                      
                }  
            }  
        }  
 
        private EnvironmentsList RetrieveData(Stream xmlStream)  
        {  
            XmlSerializer serializer = new XmlSerializer(typeof(EnvironmentsList));  
            StreamReader reader = new StreamReader(xmlStream);  
            EnvironmentsList list = (EnvironmentsList)serializer.Deserialize(reader);  
            return list;  
        }  
 
        private static void Log(string message, System.Diagnostics.TraceEventType severity)  
        {  
            LogEntry entry = new LogEntry();  
            entry.Severity = severity;  
            entry.Message = message;  
            entry.Title = "VistaAdminConfiguration";  
            entry.Categories.Add("Configuration");  
            Logger.Write(entry);  
        }  
 
        public static void SaveData(Environment environment)  
        {  
            try 
            {  
                Assembly executingAssembly = Assembly.GetExecutingAssembly();  
                string xmlFile = Path.GetDirectoryName(executingAssembly.Location) + @"\DataSources\Environments.xml";  
                  
                XmlSerializer serializer = new XmlSerializer(typeof(EnvironmentsList));  
                StreamWriter writer = new StreamWriter(xmlFile);  
                serializer.Serialize(writer, environment);  
            }  
            catch (Exception ex)  
            {  
                Log("Error trying to save Enviroment: " + ex.Message, System.Diagnostics.TraceEventType.Error);  
            }  
        }  
    }  

External XML File
<?xml version="1.0" encoding="utf-8" ?> 
<EnvironmentList> 
  <Environment Name="Environment Name" Servername="Server" Username="sa" Password="!@#!@$!@%#" Databasename="DatabaseName" ServerType="SQL" RootPath="C:\Temp"/>    
</EnvironmentList> 

3 Answers, 1 is accepted

Sort by
0
Tsvyatko
Telerik team
answered on 11 Mar 2010, 12:19 PM
Hello Garry Clark,

I have created example based on the code snippets sent. Please not the following:

 - Since Environment class represent part of your data model you may prefer to use INotifyPropertyChanged rather than inheriting DependencyObject.(Using DependancyObject will get your inherithance, using it will bond you more to the UI rather than data, objects will be accessible only from the thread that created them)

 - If you like to bind you left panel with data you need to provide way to get it (E.g. using DataContext of the parent container). In the example the StackPanel DataContext is bound to the current selected item providing the connection between the grid and the text boxes without code behind.

I hope this will help in your case. however if you have any other questions or issues do not hesitate tyo contact us.

Regards,
Tsvyatko
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
0
Garry
Top achievements
Rank 1
answered on 11 Mar 2010, 05:25 PM
Tsvyatko,
Thank you for your reply and sample application. I am glad it only required setting the DataContext of the Stackpanel and changing from DependencyProperties to INotifiedProperty. I am still trying to get my teeth cut on the WPF stuff along with MVVM so I really do appreciate your help. I still find myself getting confused as to when to use what when creating my classes that will be used for binding.

If I could trouble you with one other general WPF question though. Now that my RadGridView and Textboxes are in synch what is the best way to write the changed/new data back to the XML file. I have a "work in progress" SaveData method in the XMLDataSource class, but since I am using a XAML Resource to bind my controls to I am not quite sure at how to get at that source from my presentationmodel and call its SaveData method and pass it the updated ObservableCollection.

Also along those same lines what is the best method to determine if the RadGridView has data and if so select the first row so that the textboxes get populated OnLoad?

Thanks again Tsvyatko!
0
Tsvyatko
Telerik team
answered on 16 Mar 2010, 01:38 PM
Hello Garry Clark,

In order to apply custom action to the GridView and not write code in code behind one can take advantage of WPF attached Behavior pattern. I have created simple GridViewSelectionBehaviour class which selected first row if the GridView has data.

About the second question, I recommend you go with the Command pattern (e.g. using ICommand interface) to separate the view from the logic.The approach implemented uses simple implementation of ICommand interface.

Please let me know if you have any other questions or issues.

Regards,
Tsvyatko
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
Tags
General Discussions
Asked by
Garry
Top achievements
Rank 1
Answers by
Tsvyatko
Telerik team
Garry
Top achievements
Rank 1
Share this question
or