WPF SettingsDialog using a ListView's SelectedItem and a ContentPresenter - c#

I thought I cooked up something rather brilliant, but it's not working (yet!).
I am trying to get rid of all code behind in a SettingsDialog where a SettingsGroup can be selected by the user, which will consequently be visualized to the right of ListView (a similar settings dialog as Visual Studio has).
I was tipped to use a ContentPresenter, which I did, but then later realized that the tipper probably meant to rely on only XAML to get the job done.
This is what I got so far.
Declaring the SettingsGroups in XAML:
<Grid.Resources>
<w:DefinePathsUserControl x:Key="DefinePathSettingsGroup"></w:DefinePathsUserControl>
<w:HideShowTvShowsUserControl x:Key="HideShowTvShowsSettingsGroup"></w:HideShowTvShowsUserControl>
</Grid.Resources>
Add the left hand side SettingsGroup selector (a simple ListView):
<ListView
x:Name="SettingsGroupSelector"
Grid.Row="0"
Grid.Column="0">
<ListViewItem
x:Name="PathSetting"
Tag="{StaticResource DefinePathSettings}"
Content="Path"/>
<ListViewItem
x:Name="HideShowTvShowsSetting"
Tag="{StaticResource HideShowTvShowsSettings}"
Content="Hide/Show TV Shows"/>
</ListView>
And then I thought I could simply bind the ContentPresenter to the tag of the selected item in the ListView, like:
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding Source=SettingsGroupSelector, Path=SelectedItem.Tag}" />
Unfortunately for me, this does not show anything in my ContentPresenter. I also don't get any errors in my output window.
Who can help me further?

Change
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding Source=SettingsGroupSelector, Path=SelectedItem.Tag}" />
to this
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding ElementName=SettingsGroupSelector, Path=SelectedItem.Tag}" />
More information on specifying the Binding Source

Related

Binding textblock text from resources wpf

<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ElementName=txbValueY,Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
I try this above code but i cant to bind the text, how can i bind inside resources textblock text to outside the resources, Thanks
I am guessing that you are trying to show Text present in TextBlock resource in your second non-resource TextBlock.
You don't need DataTemplate. As you will progress ahead in WPF journey, you will come to know about those.
Below code will show "Resource Text" in your second TextBlock.
<Grid.Resources>
<TextBlock x:Key="TbRes1" Text="Resource Text" x:Name="txbValueY" Margin="5" FontSize="11" FontWeight="Medium"/>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource TbRes1},Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
All sorts of problems here:
You're specifying Mode.TwoWay in your TextBlock Text binding, it should be Mode.OneWay.
You're binding to the Label's Text property. Label doesn't have a Text property, only Content. And it's not a dependency property so you can't bind to it. (That said, a fluke of the internal mechanics does cause it to appear to work under certain conditions).
A template is exactly that: a template. You can't bind to something that doesn't exist, so the binding is meaningless.
Maybe you could clarify exactly what it is you're trying to do so we can suggest an alternative way of achieving it? Specifically, show us exactly how you're instantiating that DataTemplate.
UPDATE:
You need the first textbox to be created in order for the second one to bind to it, simply declaring it inside a DataTemplate doesn't cause that to happen by itself, so the direct binding will fail. Binding UI elements together like this should generally be avoided though, why can't you simply give the second textbox the same binding as the first?
<Grid>
<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ValueX}" Background="Orange" Foreground="White"/>
</Grid>
If for some reason this isn't possible then you can also create a binding proxy object (see this page for details):
<Grid>
<Grid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding ValueX}" />
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource ResourceKey=proxy}, Path=Data}" Background="Orange" Foreground="White"/>
</Grid>
Again, there are ways to bind to the data template declaration if you really want to, but to do that I'd have to see details of how your data template is being created at runtime.

Two longlistselectors or two columns of different data binding within longlistselector. Possible?

I am trying to figure out how to have two columns of different binded data on one page. The left column for sounds the right for a save ringtone task for each sound.
I can't put two longlistselectors on one page, it wont let me.
Using a sample, its easy to see how to used binded data for the sound. And the great thing is you only have to enter new code into the binded items and it automatically populates each page with new sound tiles.
Id like to add a save ringtone tile that would essentially work the same way. But it would only make sense if I can get the save ringtone tiles next to the sound tiles on the same page.
Is there any way to do this? All I really need to know, I think, is how to get two columns of different data bindings onto the same page, hopefully in a longlistselector so it will scroll.
Here is a sample of the code im using now.
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="SoundTileDataTemplate">
<Grid Background="{StaticResource PhoneAccentBrush}"
Margin="0,0,135,0">
<Grid VerticalAlignment="Top"
HorizontalAlignment="right"
Width="40"
Height="40"
Margin="0, 6, 6, 0">
<Ellipse Stroke="{StaticResource PhoneForegroundBrush}"
StrokeThickness="3"/>
<Image Source="/Assets/AppBar/Play.png" />
</Grid>
<StackPanel VerticalAlignment="bottom">
<TextBlock Text="{Binding Title}"
Margin="6,0,0,6"/>
</StackPanel>
</Grid>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!--Pivot Control-->
<phone:Pivot Title="{Binding Path=LocalizedResources.ApplicationTitle,
Source={StaticResource LocalizedStrings}}">
<!--Pivot item one-->
<phone:PivotItem Header="{Binding Animals.Title}">
<!--Double line list with text wrapping-->
<phone:LongListSelector Margin="0,0,-12,0"
ItemsSource="{Binding Animals.Items}"
LayoutMode="List"
ItemTemplate="{StaticResource SoundTileDataTemplate}"
SelectionChanged="LongListSelector_SelectionChanged">
</phone:LongListSelector>
</phone:PivotItem>
</phone:Pivot>
</Grid>
Easy solution.
<DataTemplate x:Key="NewItemTemplate">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" >
<StackPanel Orientation="Horizontal" Width="56">
<CheckBox x:Name="CheckBox1" HorizontalAlignment="Left" IsChecked="{Binding Checked, Mode=TwoWay}" BorderBrush="Black" Style="{StaticResource CheckBoxStyleGrey1}" Width="90" Height="74" />
</StackPanel>
<StackPanel Orientation="Horizontal" RenderTransformOrigin="0.5,0.5" Width="803" >
<StackPanel.RenderTransform>
<CompositeTransform ScaleX="-1"/>
</StackPanel.RenderTransform>
<TextBlock Text="{Binding lItem}" Foreground="Black" FontSize="45" Margin="-176,0,0,0" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<CompositeTransform ScaleX="-1"/>
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Text="{Binding lCategory}" Foreground="Black" Margin="-146,0,-2,0" RenderTransformOrigin="0.5,0.5" >
<TextBlock.RenderTransform>
<CompositeTransform ScaleX="-1"/>
</TextBlock.RenderTransform>
</TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
Edit the ItemTemplate based on your needs, and you might have to play around with it in blend if there is an error. In Blend, go to your long list selector and edit the item template.
First of all, by aiming to add 2 long list selectors next to each other, you are approaching to this problem from a very wrong perspective. That's bad for the user, bad for UX, bad for the sake of UI design and bad for the unicorns.
You are trying to associate a functionality (Save ringtone) within another LongListSelector, to the corresponding Item in another Long List Selector. What an earth made you think that adding another Long List Selector and populating it with many Save Ringtone buttons is going to solve your problem? For a second, let's say you somehow achieve adding two Long List Selectors next to each other and deployed your items on the left selector and save ringtone buttons on the right. How you are planning to correctly associate them when they are scrolled? User will scroll the left one and the right Long List Selector will remain static.
You shouldn't add one more Long List Selector to your front. Instead you should go and modify your ItemTemplate in one Long List Selector. Then you will be able to have more than one tile, button, text or whatever you need for one single LongListSelector Item.
ItemTemplate="{StaticResource SoundTileDataTemplate}"
I am not going to submit a solution to add more than one button/tile/text for one LongListSelector item and associate their communication/functionality. Because there are already some 5 million example on the internet about this.
I highly recommend reading Design Guidelines for Windows Phone for you. Because you have such ideas that will result as one more crappy app on the Store. People really got enough of crappy apps. So please either completely stop developing apps for Windows Phone or give a break to whatever you are doing now and go read the design principles.

WPF Listbox : How to bind data?

I have a listBox and add data to it like so:
for (int x = 0; x < Orchestrator.Instance.getTopicCount(); x++)
{
listTopics.Items.Add(Orchestrator.Instance.getTopic(x));
}
But I need to be able to do things like have text wrapping and divider lines, so I would like to do it the XAML.
Microsoft shows this:
<TextBlock Width="248" Height="24"
Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"
x:Name="tbSelectedColor"
Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"/>
But I don't really understand it. Here is my XAML:
<ListBox Height="261" HorizontalAlignment="Left" Margin="352,38,0,0" Name="listContent" VerticalAlignment="Top" Width="391" Grid.Column="1" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" MaxWidth="391" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
How am I able to achieve what I want? (Divider lines, text wrapping so I don't have to scroll horizontally, and data binding)
To specify the layout of each item, there are two different things you can change: the ItemContainerStyle, which provides the ControlTemplate used for each ListBoxItem, or the ItemTemplate, which provides the DataTemplate that is used to render each data item. The ItemTemplate is simpler to use if you're just getting started and haven't gotten the hang of ControlTemplates yet.
To get your text to wrap, there are two key things to do. Turn off the default horizontal scrolling of ListBox (which you got already), and set the TextWrapping property of your TextBlock to Wrap. To get to the TextBlock you need to define it in your ItemTemplate. Here's a simple example of the template declared inline, though you could also pull it out as a Resource. For the dividing line I used the simplest approach of a Border with only a bottom black line.
<ListBox x:Name="listContent" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="Black">
<TextBlock Text="{Binding}" TextWrapping="Wrap"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It seems that you will have to ascend the rather steep learning curve for how to use databinding in WPF first. Thereafter you should learn about DataTemplates and ItemTemplates to get the dividers and so forth.
Try this to get you started
A book I can heartily recommend is WPF in Action from Manning.
You need to define the ItemTemplate of your ListBox.
In your resources, add this:
<DataTemplate x:Key="myItemTemplate" TargetType="ListBoxItem">
<TextBlock Text="{Binding}"/>
</DataTemplate>
Supposing that your getTopic method returns a string, otherwise use {Binding MyTopicProperty} where MyTopicProperty is a property in your Topic class. Customize the TextBlock as you need.
Then use your ListBox like this:
<ListBox ItemTemplate="{StaticResource myItemTemplate"/>
here is an example how to bind listbox with RSS feed with DataTemplate:
<UserControl.Resources>
<XmlDataProvider x:Key ="DataRSS" XPath="//item" Source="http://rss.feedsportal.com/c/629/f/502199/index.rss">< /XmlDataProvider>
</UserControl.Resources>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ListBox ItemsSource="{Binding Source={StaticResource DataRSS}}" Height="516" Margin="0,0,32,0" Background="{x:Null}" BorderBrush="#FF627DAE">
<ListBox.ItemTemplate >
<DataTemplate >
<Grid Width="400" Height="100" >
<Image Source="{Binding XPath=enclosure/#url}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock TextWrapping="Wrap" Text="{Binding XPath=title}" FontWeight="Bold" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</grid>

Get element inside a DataTemplate

I have a following XAML and I need to change visibility of imageRemoveButton at runtime from the code behind file. How do I access that button?
<ItemsControl x:Name="ImagesItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#ffdddddd"
BorderThickness="0,0,0,1">
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="1"
VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"
TextWrapping="Wrap" />
</Grid>
<Button Grid.Column="3"
Width="20"
Height="20"
Content="X"
Template="{StaticResource ButtonAddTab}"
Style="{StaticResource ButtonWizard}"
Tag="{Binding}"
x:Name="imageRemoveButton"
Click="ImageRemoveButton_Click" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use the loaded event on the button to save a reference to the button in the cs file. Use this reference when needed to change the visibility.
You cannot reference-by-name elements in a template. There is no matching code-behind/designer property generated by templates.
You want to bind the visibility of a control in the template to a property of your data items. That means you will need to add a property to your data items. Can you provide more details of your data items? If the property is a bool value, use a VisibilityConvertor (dime a dozen on this site).
Think of templates as wanting to always pull settings from the bindings, rather than have settings stuffed into them from the outside.
Try using Source binding to a class that will manage the button's visibility state. Adding a property to all your data items would be painfull but could be required if the button's visibility needs to be set based on a property of your data item. As per standard MVVM patterns you can then control the button's visibility from C#. If you need a specific instance of the button you can add a Loaded event handler to it and cache it accordingly.

How to group controls inside Grid for reuse in XAML?

In my application I have some controls that logically belongs together and is reused many places in different windows. The controls are always placed inside a grid.
Instead of copying the controls (and the code behind) each time I want to use them, I would like to define and maintain them in a single xaml file as a single UserControl.
I have this now:
<Grid>
<Grid.ColumnDefinitions>
[ColumnDefinitions...]
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
[RowDefinitions...]
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
[More controls..]
</Grid>
But I want to reuse this part:
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
as a single control - but how do I define the Grid.Column when using the control (somehow supplying it as a parameter)? - and how do I set the Grid.RowSpan value (eventhough the code is moved to a new xaml file, and not defined inside a grid)?
Any comments?
Make them into a separate usercontrol, then include that in your project.
If you're using Blend, it's really easy, just select all the controls, right click and Make into Usercontrol.
You could also make it into a resource.
Define it in a ResourceDictionary and include the dictionary the places you want to use it.
There is one catch - the resource dictionary returns the same instance everytime - so you have to add the attribute x:Shared="false".
But the wpf way is to figure out how you can do it with a DataTemplate :)

Categories