How can I underline an ListBoxItem in WPF? - wpf

How can I underline an ListBoxItem in WPF? I'm using the following but the underline does not appear.
<DataTemplate x:Key="Phrase_List">
<ListBoxItem IsSelected="{Binding IsDefault}">
<TextBlock Text="{Binding Path=Phrase}" Tag="{Binding Path=ID}" TextDecorations="Underline" />
</ListBoxItem>
</DataTemplate>

I don't know what code you are trying to use. Please try to complete your question. I've used the following code to underline the 'World' item in my little ListBox.
<ListBox>
<ListBoxItem>Hello</ListBoxItem>
<ListBoxItem>
<Underline>World</Underline>
</ListBoxItem>
</ListBox>

You can use a textblock, and set the textdecorations property to underline. Remember the contents of a ListBoxItem can be things other than text, hence why it's not a simple case of setting some property on the ListBoxItem.

You will need to create an item template that displays your text in a TextBlock control. On the TextBlock, set the TextDecorations property (which is a collection) to contain 'Underline'.

In XAML:
<ListBox Name="lst">
<ListBoxItem Content="item1" />
<ListBoxItem Content="item2" FontStyle="Italic" FontWeight="Normal" />
</ListBox>
In C#:
lst.Items.Clear();
lst.Items.Add(new ListBoxItem { Content = "item 1" });
lst.Items.Add(new ListBoxItem { Content = "item 2" });
lst.Items.Add(new ListBoxItem { Content = "item 3" });
ListBoxItem l = (ListBoxItem)lstItems.Items[2];
li.SetValue(TextElement.FontStyleProperty, FontStyles.Italic);

Related

ItemsControl DataTemplate Items showing side by side

I've been researching this for quite long time now, can't find the answer.
How can I display each item in my itemscontrol side by side?
The following code displays each item's content side by side (label and textbox), but the next item is shown underneath. Let's say I have 3 items in my ItemsControl. The current behaviour is:
Label Textbox
Label Textbox
Label Textbox
What I want is:
Label Textbox Label Textbox Label Textbox (side by side)
The current code uses a stack panel whick sets the orientation to horizontal (that's why the label and textbox are side by side). But I need some property or technique to set the itemscontrol content orientation to horizontal. My code:
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Name="pnlText" Orientation="Horizontal" Width="750">
<Label Content="{Binding ParameterDisplayName, Mode=OneWay}" />
<TextBox Name="txtText" HorizontalAlignment="Left" Text="{Binding ParameterValue, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}" Visibility="{Binding ParameterType, Converter={StaticResource ParameterTypeToVisibilityConverter}, ConverterParameter=Text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
Does anyone know how to do that?
Thanks!
You should set this property for your ItemsControl:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

windows phone 7 TextBlock TextWrapping not honored in listbox

I have a listbox defined as :
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="myListBox" Width="468" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Template>
<ControlTemplate>
<ScrollViewer Width="468">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
</ListBox>
</Grid>
In the code, I create multiple textBlocks as the Listbox Items with textWrapping enabled in each textBlock.
for (int i = 0; i < everyLine.Length; i++)
{
TextBlock txtBlock = new TextBlock()
{
TextWrapping = TextWrapping.Wrap,
Name = "textBlock" + i,
Foreground = textBrush,
FontSize = 20,
Text = everyLine[i]
};
this.myListBox.Items.Add(txtBlock);
}
But, none of the text in any of the text blocks gets wrapped.
Can somebody please let me know if the above way of defining textBlocks in listbox is incorrect?
+1 for Derek's answer
Also, please be careful using the <StackPanel> in your ListBox. By default, the ListBox uses a <VirtualizingStackPanel> and this is very important as it uses significantly less UI resources (memory) when displaying long lists.
Is there any particular reason why you are adding elements in code? By the looks of things you have a data collection, which you can set ast teh ItemsSource of the ListBox and then use an ItemTemplate to specify what each item should look like. Something like the following:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="myListBox" Width="468">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="20" Text="{Binding}" TextWrapping="Wrap" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Note, that the default style for the ListBox already includes the ScrollViewer so there's no need to change the ControlTemplate. Because you've already fixed the width of the ListBox, the above should "just work".

Pattern for working with a form in Silverlight 4? (How to get references to XAML elements)

I have a form:
<StackPanel Orientation="Horizontal" Visibility="{Binding Editable, Converter={StaticResource visibilityConverter}}"
ToolTipService.ToolTip="Add new topic to this group">
<sdk:AutoCompleteBox Width="160" ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.TopicNames}" />
<Button Click="addTopicButton_Click">
<Image Source="Images/appbar.add.rest.png" />
</Button>
</StackPanel>
This form appears in a DataTemplate for an ItemsControl. I'm not sure what the best way is to get the data from the AutoCompleteBox when the button is clicked. I can't give the elements x:Name attributes, because they're in a template (right?).
How can I get around this? The Click event will give me the Button, but I need a reference to the text box. Use the Button's parent, then look through the children for the Textbox? If I factored this out into its own UserControl, I could set x:Name values, but I'd rather not do that.
Any other ideas?
Update: Here is another example of such a problem:
<ListBox x:Name="topicList"
ItemsSource="{Binding Id, Converter={StaticResource topicGroupIDConverter}}"
SelectionChanged="ListBox_SelectionChanged"
HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
Width="150"
VerticalAlignment="Center"
ToolTipService.ToolTip="{Binding Description}"
ToolTipService.Placement="Right" />
<Button ToolTipService.ToolTip="Remove this topic from this group"
Visibility="{Binding ElementName=topicList,
Path=DataContext.Editable,
Converter={StaticResource visibilityConverter}}"
Click="removeTopicButton_Click"
HorizontalAlignment="Right"
Margin="10,0">
<Image Source="Images/appbar.cancel.rest.png" />
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When the button is clicked, I want to access topicList.DataContext. However, topicList itself is a DataTemplate in an ItemsControl, so I can't access it using its name from code-behind. How else can I do this?
You can add a property, say SelectedItemInAutoCompleteBox, to your presenter, and then can bind it to the SelectedItem property of AutoCompleteBox, using Mode=TwoWay, like this,
<sdk:AutoCompleteBox SelectedItem="{Binding Path=DataContext.SelectedItemInAutoCompleteBox, Mode=TwoWay}" ... />
You may try the same approach with Text property of AutoCompleteBox, also. See if it solves your problem.:-)
You have several choices:
If you're on Silverlight 5, use the AncestorBinding
Otherwise, use a Silverlight 4 AncestorBinding hack (it doesn't look pretty)
Or you could try DataContextProxy, which stores the DataContext in a resource so that it is accessible. Note: you should set the DataContextProxy as a Resource of topicList ListBox, not the UserControl as in Dan Wahlin's example.

Focus movement between list and other elements. WPF

In my window I have the listview and other elements. How I can achieve such behavior of focus: when I press downkey on last listitem focus moves from listview on another element, accordingly pressing upkey on first element moves focus away from list.
So focus on pressing up-downkeys can move from other elements to the list, pass throw the list and leave from the list.
Thanks in advance.
Set the KeyboardNavigation.DirectionalNavigation attached property to Continue. The default for ListBox is Contained.
<ListBox KeyboardNavigation.DirectionalNavigation="Continue"/>
I have different situations in my application. For all controls I have my styles, but in style for the tabcontrol I set property KeyboardNavigation.DirectionalNavigation="Continue".
1. Here should have possibility to be selected listviewitems and tabitems. And when focus on tabitem and I press (->) tabitem changes.
2. Here should have possibility to be selected listboxitems and buttons.
3. Here should have possibility to be selected TabItem and TextBlocks.
And I can use just arrows not tab. 1:
<TabControl Name="tabControl1">
<TabItem Header="PORT" Name="PORT">
<ListView x:Name="ATList"
ItemsSource="{Binding Path=., NotifyOnTargetUpdated=true}"
KeyboardNavigation.DirectionalNavigation="Continue"
KeyUp="ATList_KeyUp" TargetUpdated="ATList_TargetUpdated">
<ListView.View>
<GridView >
<GridViewColumn> ........
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</TabItem>
.......
</TabControl>
2.
<Grid>
<ListBox ItemsSource="{Binding NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}"
KeyboardNavigation.DirectionalNavigation="Continue"
ClipToBounds="true" TargetUpdated="List_TargetUpdated" SourceUpdated="List_SourceUpdated">
</ListBox>
<WrapPanel>
<Label .../>
<Label .../>
<Button x:Name="NewBtn"
IsEnabled="{Binding ElementName=UsedMemory, Path = Content, Converter={StaticResource RouteNewConverter}}"
Click="NewRouteBtn_Click"> New </Button>
<Button x:Name="DeleteBtn" Click="DeleteBtn_Click"> Delete </Button>
</WrapPanel>
</Grid>
3.
<TabControl Margin="2,5,2,1" Name="tabControl1" >
<TabItem Header="AV" Name="AV">
<Grid>
<StackPanel x:Name="inf">
<TextBlock />
...
<TextBlock />
</StackPanel>
</Grid>
</TabItem>
</TabControl>

Controlling the TabControl active tab with a ComboBox

What i am really trying to achieve is to full control the active TabItem by using the combobox as the navigation control.
Here is what ive got so far:
<TabControl Canvas.Left="26" Canvas.Top="27" Height="100" Name="TabControl1" Width="220">
<TabItem Header="TabItem1" x:Name="TabItem1">
<Grid />
</TabItem>
<TabItem Header="TabItem2" x:Name="TabItem2">
<Grid />
</TabItem>
</TabControl>
<ComboBox Canvas.Left="126" Canvas.Top="134" Height="23" Name="CmbTabs" Width="120"
ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
SelectedValue="{Binding ElementName=TabControl1, Path=SelectedIndex}"
SelectedValuePath="TabIndex"
DisplayMemberPath="Header"/>
Still the only thing that actual works is the list that shows up when i press the togglebutton of the combobox.
Even selecting a tabitem name through the list does not do anything, it does not even update the selected value textbox of the combobox.
Any help ?
Edit:
Ok the answer of Steve Robbins worked fine for the "controling" issue.
What about the fact that selecting an item in the combobox drop down list does not update the value of the combobox? (the comboboxes textbox is still blank!!)
If you're trying to control the TabControl from the Combo then it looks a bit backwards to me.. if you change the SelectedIndex on the tab control to bind it to the combo it should work:
<TabControl Canvas.Left="26" Canvas.Top="27" Height="100" Name="TabControl1" Width="220" SelectedIndex="{Binding ElementName=CmbTabs, Path=SelectedIndex}">
<TabItem Header="TabItem1" x:Name="TabItem1">
<Grid />
</TabItem>
<TabItem Header="TabItem2" x:Name="TabItem2">
<Grid />
</TabItem>
</TabControl>
I started looking at this because of some problems I'm having with the combobox. Unfortunately, I haven't solved my problem but I can provide some additional insight and a workaround to this problem. First off, lets start with my changes to the original xaml.
<TabControl Height="100" Name="TabControl1" Width="220">
<TabItem Header="TabItem1" x:Name="TabItem1">
<TextBlock Text="TabItem1 Content" />
</TabItem>
<TabItem Header="TabItem2" x:Name="TabItem2">
<TextBlock Text="TabItem2 Content" />
</TabItem>
</TabControl>
<ComboBox Height="23" Name="CmbTabs" Width="120"
ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"
DisplayMemberPath="Name"
>
</ComboBox>
Notice that instead of creating a binding from the tab control to the ComboBox and vice versa we can create a TwoWay binding (the default in this case) between the SelectedIndex of the tab and combo controls. Next, let's add some content to the TabItems. At this point, similar to Steve's suggestion, we've fixed the "controling" problem. That is, changing the selected TabItem changes the selected ComboBox item (you'll have to trust me on this one or keep reading!) and changing the ComboBox changes the selected TabItem. Great!
The above xaml also changes DiplayMemberPath property to "Name". I think you will find that this eliminates hughdbrown's "weird result". Recall that the Header property (an object) is wrapped by a ContentPresenter. I believe that if no template is supplied the default behavior is to display the Header object as a string in a TextBlock. Thus, the "weird result" correctly reports that the TextBlock control does not contain a Header property.
Now let's make some changes to the previous ComboBox xaml.
<ComboBox Height="23" Name="CmbTabs" Width="120"
ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This actually produces an interesting result and makes use of the content we added to each TabItem. When this code runs, you'll notice that the ComboBox now displays the selected TabItem's content. Furthermore, the list now displays "System.Windows.Controls.TabItem..." for each TabItem. We can change the TextBlock binding to {Binding Header} and display the Header object but the ComboBox still displays the selected TabItem's content. As it is late on a Friday evening and there just is not enough beer in the world, I didn't look into possible reasons for this. However, I do have a workaround!
First, let's create a ValueConverter to convert the TabControl's Items collection to something we can use. Here's the code.
public class TabItemCollectionConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ItemCollection collection = value as ItemCollection;
IList<string> names = new List<string>();
foreach (TabItem ti in collection.SourceCollection)
{
names.Add(ti.Header.ToString());
}
return names;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
The converter simply creates a new collection from the TabControl's Items collection that contains the string-ized Header object from each TabItem. This works fine for simple Header objects but obviously has limitations. Now let's consider how we use this in the xaml.
<ComboBox Height="23" Name="CmbTabs" Width="120"
ItemsSource="{Binding ElementName=TabControl1, Path=Items, Converter={StaticResource ItemConverter}}"
SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Remember that a ValueConverter used in an ItemsSource binding returns a new collection. In this case we are converting the TabControl's Items collection to a string collection. Don't forget to create the converter StaticResource! It looks something like this.
<local:TabItemCollectionConverter x:Key="ItemConverter"/>
Now, using the converter, the whole ball of wax works as expected.
What still puzzles me is why the ComboBox displays the TabItem Headers in the list and the TabItem content as the selection. No doubt there is some obvious explanation but as I said, it's Friday...
Hope this helps!
I've been playing with this a lot. There is something about the source of the ComboBox's databinding changing after the selection is made. Or something.
I added the Diagnostics namespace:
xmlns:debug="clr-namespace:System.Diagnostics;assembly=WindowsBase"
And I changed your <Grid /> into TextBoxes with big honking numbers so that I could see that things really were changing:
<TabItem Header="TabItem1" x:Name="TabItem1">
<TextBlock Name="tb1" FontSize="24" Text="1" Width="100" Height="26" />
</TabItem>
And when I ran the app, I found that Diagnostics reports a weird result:
System.Windows.Data Error: 39 :
BindingExpression path error: 'Header'
property not found on 'object'
''TextBlock' (Name='tb1')'.
BindingExpression:Path=Header;
DataItem='TextBlock' (Name='tb2');
target element is 'TextBlock'
(Name=''); target property is 'Text'
(type 'String')
I tried to set the databinding once, in code, at startup:
public Window1()
{
InitializeComponent();
Binding positionBinding = new Binding("Items");
positionBinding.ElementName = "TabControl1";
positionBinding.Path = new PropertyPath("Items");
positionBinding.Mode = BindingMode.OneTime;
CmbTabs.SetBinding(ComboBox.ItemsSourceProperty, positionBinding);
CmbTabs.DisplayMemberPath = "Header";
}
And it still switches the CombobBox to show no selected item after the selection and change of TabItem is made. It's as if the DataContext is switched to the TabControl.TabItem.TextBlock after the TabControl changes selection.
So I don't exactly have an answer for you but I have some results for you to work on.
Bea Stollnitz has a good article on using this diagnostic technique. "How can I debug WPF bindings?"
Building off of hughdbrown's answer, I found this post on MSDN that describes your problem as a bug. You can reproduce it with this XAML (which has the opposite problem as your XAML):
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl Name="TabControl1" Width="220" Height="100" Canvas.Left="26" Canvas.Top="27">
<TabItem x:Name="TabItem1" Header="TabItem1">
foobar
</TabItem>
<TabItem x:Name="TabItem2" Header="TabItem2">
fizzbuzz
</TabItem>
</TabControl>
<ComboBox Name="CmbTabs" Width="120" Height="23" Canvas.Left="126" Canvas.Top="134"
ItemsSource="{Binding ElementName=TabControl1, Path=Items}"
DisplayMemberPath="Length"
SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}"/>
</Canvas>
As you can see the Length binding works fine except for in the dropdown, where it is going off the TabItem instead of the string inside.
I'm not sure it's ideal for your purposes, but you can get around it by being a little less elegant and reproducing your headers in ComboBoxItems:
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl Name="TabControl1" Width="220" Height="100" Canvas.Left="26" Canvas.Top="27">
<TabItem x:Name="TabItem1" Header="TabItem1">
<Grid/>
</TabItem>
<TabItem x:Name="TabItem2" Header="TabItem2">
<Grid/>
</TabItem>
</TabControl>
<ComboBox Name="CmbTabs" Width="120" Height="23" Canvas.Left="126" Canvas.Top="134"
SelectedIndex="{Binding ElementName=TabControl1, Path=SelectedIndex}">
<ComboBoxItem>TabItem1</ComboBoxItem>
<ComboBoxItem>TabItem2</ComboBoxItem>
</ComboBox>
</Canvas>

Resources