views:

126

answers:

3

Hi, I hope to find an answer on how to achieve functionality I am trying to have. I have two listboxes. I generate content for them from XML. I also define UriSources in XML. I'd like to find out how to achieve a navigation by clicking any listbox item in any listbox. As a result, I need to be able to update Frame's Source property from two different lisboxes. Perphaps, it should be multibind with some converter. Any ideas are highly appreciated.

XAML: Listboxes and Frame:

<Window
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" 
mc:Ignorable="d"
x:Class="TwoListboxes_2.MainWindow"
xmlns:local="clr-namespace:TwoListboxes_2"
Title="MainWindow"
Width="1000" Height="700">
<Window.Resources>
    <local:UriConverter x:Key="UriConverter" /> 
    <XmlDataProvider x:Key="PageData" Source="Data/data.xml" XPath="/Pages" />
    <DataTemplate x:Key="SublevelListboxDataTemplate">
        <Grid>
            <TextBlock Text="{Binding XPath=@name}"/>   
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="MainListBoxDataTemplate">  
            <TextBlock Text="{Binding XPath=@name}"/>   
    </DataTemplate>
</Window.Resources>
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">   
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="215"/>
            <ColumnDefinition Width="1"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid >
            <ListBox x:Name="SublevelListbox" Width="80"
                     DataContext="{Binding SelectedItem, ElementName=Nav_ListBox}"
                     ItemTemplate="{DynamicResource SublevelListboxDataTemplate}" 
                     ItemsSource="{Binding XPath=./*}" 
             />
            <Border HorizontalAlignment="Left">
                <ListBox x:Name="Nav_ListBox" 
                         ItemTemplate="{DynamicResource MainListBoxDataTemplate}" 
                         ItemsSource="{Binding Source={StaticResource PageData}, XPath=page}"
                         SelectedValuePath="@UriSource"
                         SelectedIndex="0"
                />
            </Border>
        </Grid>
        <Frame Grid.Column="2" x:Name="ContentFrame" JournalOwnership="OwnsJournal" NavigationUIVisibility="Visible" 
               Source="{Binding SelectedValue, Converter={StaticResource UriConverter}, ElementName=Nav_ListBox, Mode=TwoWay}"
            />
</Grid> 

XML:

<?xml version="1.0" encoding="utf-8"?><Pages xmlns=""> 
    <page name="Name 1" UriSource="Pages/Name1.xaml"  /> 
    <page name="Name 2" UriSource="Pages/Name2.xaml"  > 
                    <level2 name="ALL1" UriSource="Pages/All1.xaml" /> 
            <level2 name="ALL2" UriSource="Pages/All2.xaml" /> 
                    <level2 name="ALL3" UriSource="Pages/All3.xaml" /> 
            <level2 name="ALL4" UriSource="Pages/All4.xaml" /> 
    </page> 
    <page name="Name 3" UriSource="Pages/Name3.xaml"/> 
    <page name="Name 4" UriSource="Pages/Name4.xaml" IsEnabled="True" /> 

UriConverter to alllow listboxitems to be in selected state when its UriSource is loaded into Frame:

public class MultiBindConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {

        if (values[0] != null)
        {
            if (values[1] != null)
            {
                return new Uri(values[1].ToString(), UriKind.RelativeOrAbsolute);
            }
            return new Uri(values[0].ToString(), UriKind.RelativeOrAbsolute);
        }
        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        //throw new NotImplementedException();

        var uri = (Uri)value;
        var uriString = uri.OriginalString;
        if (value != null)
        {
            if (uri.OriginalString.Contains(";component/"))
            {
                uriString = uriString.Substring(uriString.IndexOf("/") + 1);
            }
        }
        return new object[] { uriString, uriString[0], uriString[1] };
    }
    #endregion IMultiValueConverter Members
} 
Thank you in advance.
+1  A: 

I think that the design is that you need to have master list bound to one list box, details list (sub-list) bound second list. The structure i.e. your data model should be,

Masters - List of details - Selected detail view

and you bind the selected detail view to the frame.

bjoshi
It is hard to comprehand without a sample. I believe that now I have something similar. I have one Master XML list that is bound to one Mainlistbox. The nodes of sublevel links are binded to another list. I bind the source of the frame to selected item - Source="{Binding ElementName=Nav_ListBox, Path=SelectedValue, Mode=TwoWay, Converter={StaticResource UriConverter}}". However, I cannot bind 'ElementName property to two listboxes. I am looking for the way on how to achieve this kind of binding.
vladc77
+1  A: 

If I Understand correctly you are able to bind two list box correctly the only problem is with binding frame with two different source. your Guess is right you can achieve this using Multibinding. i am Assuming the second listbox has no default selection.

The multibinding in xaml will be like this

<Frame Grid.Column="2" x:Name="ContentFrame" JournalOwnership="OwnsJournal" NavigationUIVisibility="Visible">
            <Frame.Source>
                <MultiBinding Converter="{StaticResource conv}">
                    <Binding Path="SelectedItem" ElementName="Nav_ListBox"/>
                    <Binding Path="SelectedItem" ElementName="SublevelListbox"/>
                </MultiBinding>
            </Frame.Source>
        </Frame>

Your Converter will be

public class MultiBindConverter : IMultiValueConverter {
        #region IMultiValueConverter Members

        public object Convert (object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            if (values[0] != null) {
                if (values[1] != null) {
                    return new Uri (values[1].ToString (), UriKind.RelativeOrAbsolute);
                }
                return new Uri (values[0].ToString (), UriKind.RelativeOrAbsolute);
            }
            return null;
        }

        public object[] ConvertBack (object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
            throw new NotImplementedException ();
        }

        #endregion IMultiValueConverter Members
    }
Fatema
Thank you for writing this converter. I have a problem. I beielve it is minor. The application stops working after build. VS error message is "Cannot locate resource 'system.xml.xmlelement'.". What can cause this errors? Any ideas? Thank you for your help.
vladc77
I changed the Path property to Path="SelectedValue". It works now. However, I use this navigation Back/Forward Navigation. Now, I lost the ability to indicate listboxitem in selected state if its UriSource is loaded into the frame, like it was before. It is because I cannot access UriConverter anymore which I used before. I added it in my code above. I am wondering if it is possible to combine both convertes into one. I tried to make it work but always getting errors on a built. Please let me know if it possible to achieve. Thank you again for your help. I highly appreciate it.
vladc77
I also change Bindings to<Binding Path="SelectedValue" Converter="{StaticResource UriConverter}" ElementName="Nav_ListBox" Mode="TwoWay" /> <Binding Path="SelectedValue" Converter="{StaticResource UriConverter}" ElementName="SublevelListbox" Mode="TwoWay" />
vladc77
However, it does not do anything with UriConverter. I really stuck here. I cannot get listboxitems be selected while navigate with Back/Forward buttons of the Frame navigation. What is the tricj to overcome this. Thank you again.
vladc77
A: 

Yes i tried it and this does not work. May be you need to keep a property in your data class through which you can bind your frame source and the selectedValue of your listbox items.

Fatema
Thank you for looking. I awarded your answer as aswered. Thank you for the help. I also modified the multibinding converter which you offered me. Now, it is working with selecting listboxitems but it is changing a selected states of items only of the first listbox with x:name="Nav_ListBox". You can look at this multibinding converter in my question above after the text:"UriConverter to alllow listboxitems to be in selected state when its UriSource is loaded into Frame:" It is much closer. Is it possible to make thre same for second listbox? I have hard time at this point.
vladc77
In my understanding when we are trying to bind frame.source to listboxitem selectedvalue and vice versa. The main problem is if we update one value the other two will get effected which will intern effect the first one. hence forming a loop. Which is impossible to break. Thats why i suggested to have a dataclass proeprty which will get updated each time any of the 3 controls is changed by the user. and then this will update the other two control.
Fatema
Thank you. I will look into it. However, I am beginner in C#. I never did this model yet.
vladc77