I do want to create a listview which consists of many items of an own class. One of the properties is a text which can contain one or more links in it. Normally I use a textblock with a binding on Text to get this content displayed.
Now I do want those text being parsed for links and then dynamically make those links clickable. I found quite some code like Add hyperlink to textblock wpf on how to create a textblock with hyperlinks so I would be fine - but the WPF binding is available on the Text property so this doesn't help me in the end.
So is there a way for binding for a list of items (ObservableCollection or similar) in a listview to have clickable links within text?
Thx in advance
Sven
I have a simple solution.
Using the DataTemplate, you could specify an template for a class, say LinkItem which contains your text, and a hyper-link.
public class LinkItem
{
public string Text { get; set; }
public string Hyperlink { get; set; }
public LinkItem(string text, string hyperlink)
{
Text = text;
Hyperlink = hyperlink;
}
}
// XAML Data template
<DataTemplate DataType="{x:Type HyperlinkDemo:LinkItem}">
<TextBlock>
<TextBlock Text="{Binding Text}" Margin="1" />
<Hyperlink>
<TextBlock Text="{Binding Hyperlink}" Margin="1" />
</Hyperlink>
</TextBlock>
</DataTemplate>
// List box definition
<ListBox ItemsSource="{Binding LinkItems}" />
Nice and simple. Just add a bunch of LinkItem to your LinkItems collection and you will get some nice mix of text and hyperlink in your list box.
You could also throw in a command in the LinkItem class to make things a little more interesting and bind the command to the hyperlink.
<Hyperlink Command="{Binding HyperlinkCommand}"> ....
Related
in my .net4-0 C# application I have a grid of 8x5 buttons. Each of them should display an image and a single letter (the hotkey to press) over the image (top left corner). Because the images depend on my data, it must be dynamically. All Images have the same size (100x100 pixel). The Image should fill the button nicely.
How can i achieve this?
My thoughts so far is, every time I load my data and change the display images, I manually create a Stackpanel, with a Image and TextBlock on it. But this doesn't fit well together.
Use a ListBox and put your list of data into ListBox.ItemSource Then you can create your own DataTemplate do specify how you want to display your data in that ListBox
For Example you can specify that you want your ListBox to be displayed with a 8x5 Grid. This of course depends on if you know that you will always display your Grid with 8x5 cells.
For the specific that you want to have a button with a letter on top as you said I would go with this.
<!--Resource-->
<DataTemplate DataType={x:YourViewModel}>
<StackPanel>
<TextBlock Content="{binding SomeText}"/>
<Button>
<Button.Content>
<Image Source="{binding YourImageSource}" Width="100px" Height="100px"/>
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
This is assuming that you're using MVVM which is highly recommended when working with WPF.
If you don't know what MVVM then start reading about it cause it will help your development in WPF to become so much better.
If my example is confusing, please provide feedback and I will make it more clearer.
EDIT Simple-MVVM Example
Let's say we want to display a Game with a title and picture
First we create the Model
public class Game
{
private string _title {get; set;}
private string _imagepath {get; set;} //We are not getting the image but the imagepath
Public string Title {get{return _title;} set{_title = value;}
Public string Imagepath set{_imagepath = value;}
}
Then we need a ViewModel. Normally the ViewModel doesn't create new data since the data should come from the Model (From maybe a Database), but for the sake of this example we create it in the ViewModel
public class GameViewModel
{
Public ObservableCollection<Game> Games {get; set;} //<--- This is where the View is binding to
public GameViewModel
{
ObservableCollection<Game> Games = new ObservableCollection<Game>(); //ObservableCollection is used to notify if we have added or removed or updated an item in the list.
Games.Add(new Game() {Title = "Warcraft 3", Image=//Where you have your images );
Games.Add(new Game() {Title = "Overwatch", Image=//Where you have your images );
this.Games = Games;
}
}
And now when to have our View to bind to this ViewModel in our XAML-code
<!--Basic xaml-->
<!--Resource-->
<DataTemplate DataType={x:Type local:Game}>
<StackPanel>
<TextBlock Content="{Binding Title}"/>
<Button>
<Button.Content>
<Image Source="{Binding Imagepath}" Width="100px" Height="100px"/>
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
Then in our ListBox
<ListBox ItemSource="{Binding Games}"/>
To get this to work you need to set the Datacontext in the View. Often when you create a new WPF-project the the View is called MainWindow.xaml
Add the ViewModel to the datacontext like this
/*Normally you want to avoid doing anything in code-behind with MVVM
If you want to avoid that you have to look into DI and IoC But it is way
to much to do in this example*/
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new GameViewModel();
}
}
I've this requirement where:
For example, a textblock has a name displayed as "Lastname, Firstname" which upon click will load two textbox controls with Firstname's value loaded into its own box and lastname's value into its own box and keep them ready for editing. While I can easily write a bunch of if conditions in my code-behind to achieve this, I'm looking for better ways of avoiding such a solution as there is no WPFism or MVVM treatment to it. Also, there are a bunch of other controls in my screen that will need this sort of a behavior and the code can soon get ugly if i resort to the initial way of solving it by just toggling visibility.
Is there a better way to solve this in WPF? Would I be able to group these controls into a custom control and define my control template (but that would mean I'll need to roll such things for all the controls in my screen.)
Any help in that direction would be great.
Thanks
Bala
Sounds like you want to get into the wonderful world of DataTemplates
first we'll create a container class for your data
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
public Person(string first, string last)
{
FirstName = first;
LastName = first;
}
}
Then, in the code behind of your hosting element (probably window). You can declare and ObservableCollection<> of the objects.
//populate me wherever!
public ObservableCollection<Person> People {get;set;}
(dont forget to initialize that somewhere to avoid all kinds of unnecessary exceptions)
Then you can bind the collection to an items collection, i like the ItemsControl
<ItemsControl ItemsSource="{Binding Path=People}"/>
Except this doesn't give the result we want. So while we could override the ToString() of our class to spit out some formatted data, we can do one better by overriding the item template of the items control.
<ItemsControl ItemsSource="{Binding Path=People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button DataContext="{Binding}" Click="Button_Click">
<Button.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{1}, {0}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Button.Content>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that I'm using a multi-binding with a string formatter in order to format the text correctly on the buttons.
Now on the Buttons Click Event handler, you can process an item. As the Sender of the event will be the button, and the DataContext of the button will be the person object it represents and you can do what you wish.
You might also find the ListBox and its SelectedItem Property very useful in your particular case.
Hopefully this is enough to get you started, -Val
I'm new to WPF and before I dive in solving a problem in completely the wrong way I was wondering if WPF is clever enough to handle something for me.
Imagine I have a collection containing objects. Each object is of the same known type and has two parameters. Name (a string) and Picked (a boolean).
The collection will be populated at run time.
I would like to build up a UI element at run time that will represent this collection as a series of checkboxes. I want the Picked parameter of any given object in the collection updated if the user changes the selected state of the checkbox.
To me, the answer is simple. I iterate accross the collection and create a new checkbox for each object, dynamically wiring up a ValueChanged event to capture when Picked should be changed.
It has occured to me, however, that I may be able to harness some unknown feature of WPF to do this better (or "properly"). For example, could data binding be employed here?
I would be very interested in anyone's thoughts.
Thanks,
E
FootNote: The structure of the collection can be changed completely to better fit any chosen solution but ultimately I will always start from, and end with, some list of string and boolean pairs.
I would strongly recommend the ItemsControl, its behaviour is as close as you can get to the ASP.Net repeater control so it is very flexible.
Declare the item control as:
<ItemsControl Name="YourItemsControl"
ItemsSource="{Binding Path=YourCollection}"
ItemTemplate="{StaticResource YourTemplate}">
</ItemsControl>
Then you can use the datatemplate to organise the data into a display format for the user
<DataTemplate x:Key="ProjectsTemplate">
<StackPanel Margin="0,0,0,10">
<Border CornerRadius="2,2,0,0" Background="{StaticResource ItemGradient}" d:LayoutOverrides="Width, Height">
<local:ItemContentsUserControl Height="30"/>
</Border>
...
Useful ItemsControl Links
http://drwpf.com/blog/itemscontrol-a-to-z/
http://www.galasoft.ch/mydotnet/articles/article-2007041201.aspx
I hope this helps you.
You can use Data Templates. Here's a good post about it.
This is exactly the kind of scenario WPF simplifies. Event-handlers- bah! Data-binding and data templates make this a cinch. I have constructed an example illustrating how you can do this.
Here is the code-behind, which declares a class to represent your items- PickedItem. I then create a collection of these items and populate it with some samples.
public partial class DataBoundCollection : Window
{
public DataBoundCollection()
{
Items = new ObservableCollection<PickedItem>();
Items.Add(new PickedItem("Item 1"));
Items.Add(new PickedItem("Item 2"));
Items.Add(new PickedItem("Item 3"));
InitializeComponent();
}
public ObservableCollection<PickedItem> Items
{
get;
set;
}
}
public class PickedItem
{
public PickedItem(string name)
{
Name = name;
Picked = false;
}
public string Name
{
get;
set;
}
public bool Picked
{
get;
set;
}
}
Now, let's look at the XAML mark-up for this window:
<Window x:Class="TestWpfApplication.DataBoundCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBoundCollection" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Picked}" Margin="5"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I create a ListBox to hold the items, and bind its ItemsSource property to the collection I created in the code-behind. Then, I provide the ListBox with an ItemTemplate, which determines how each PickedItem will be rendered. The DataTemplate in this case is as simple as a check-box and some text, both bound to the member variables on PickedItem. Now, when I check any of these items, the data in the underlying collection is modified, in real-time, with no event handlers needed. Ta-da!
alt text http://img405.imageshack.us/img405/1083/databoundcollection.png
I want to build a simple application with the MVVM pattern.
This application will have two main parts:
menu on top
content below
The navigation will be simple:
each menu item (e.g. "Manage Customers" or "View Reports") will fill the content area with a new page that has some particular functionality
I have done this before with code behind where the code-behind event-handler for menu items had all pages loaded and the one that should be displayed was loaded in as a child of a StackPanel. This, however, will not work in MVVM since you don't want to be manually filling a StackPanel but displaying e.g. a "PageItem" object with a DataTemplate, etc.
So those of you who have made a simple click-menu application like this with MVVM, what was your basic application structure? I'm thinking along these lines:
MainView.xaml:
<DockPanel LastChildFill="False">
<Menu
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"/>
<ContentControl
Content="{Binding SelectedPageItem}"/>
</DockPanel>
where the Menu is filled with a collection of "PageItems" and the DataTemplate displays the Title of each "PageItem object" as the Header of each MenuItem.
And the ContentControl will be filled with a View/ViewModel pair which has full functionality, but am not sure on this.
First, I think you should keep the code-behind event handler, there's no point in changing a simple 2 line event handler to a complex command driven monster for no practical reason (and don't say testebility, this is the main menu, it will be tested every time you run the app).
Now, if you do want to go the pure MVVM route, all you have to do it to make your menu fire a command, first, in some resource section add this style:
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding DataContext.SwitchViewCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
<Setter Property="CommandParameter"
Value="{Binding}"/>
</Style>
This style will make the menu item fire a the SwitchViewCommand on the attached view model with the MenuItem's DataContext as the command parameter.
The actual view is the same as your code with an additional reference to that style as the ItemContainerStyle (so it applies to the menu item and not the content of the DataTemplate):
<DockPanel LastChildFill="False">
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"
ItemContainerStyle="{StaticResource MenuItemStyle}"/>
<ContentControl
Content="{Binding SelectedPageItem}"/>
</DockPanel>
Now in the view model you need (I used strings because I don't have your PageItem code):
private string _selectedViewItem;
public List<string> PageItemsMainMenu { get; set; }
public string SelectedPageItem
{
get { return _selectedViewItem; }
set { _selectedViewItem = value; OnNotifyPropertyChanged("SelectedPageItem"); }
}
public ICommand SwitchViewCommand { get; set; }
And use whatever command class you use to make the command call this code:
private void DoSwitchViewCommand(object parameter)
{
SelectedPageItem = (string)parameter;
}
Now, when the user clicks a menu item the menu item will call the SwitchViewCommand with the page item as the parameter.
The command will call the DoSwitchViewCommand that will set the SelectedPageItem property
The property will raise the NotifyPropertyChanged that will make the UI update via data binding.
Or, you can write a 2 line event handler, your choice
i could imagine an ObservableCollection in the VM, that holds all the pages to be callable from the menu.
Then bind an ItemsControl And the ContentControl to it to make the ContentControl always show the CurrentItem from that List.
Of course, the menu will only bind to some Title property
whereas the ContentControl will adopt the whole item and plug in some appropriate view according to the type.
Another option is to use a ListBox instead of a menu, style the ListBox to look like a menu and then you can bind to the selected value, like this:
<DockPanel LastChildFill="False">
<ListBox
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"
IsSynchronizedWithCurrentItem="True"/>
<ContentControl
Content="{Binding PageItemsMainMenu/}"/>
</DockPanel>
Note the IsSynchronizedWithCurrentItem="True" to set the selected item and the {Binding PageItemsMainMenu/} with the trailing slash to use it.
I feel really stupid for asking this but I have been thrashing for over 8 hours. How do I get the Selected Item to show its text in my WPF combo box when selected?
Above is an option dialog that allows users to select and configure the available tournament displays. The problem is the selected combo box item shows the UserControl instead of the Display name.
On Window Loaded:
//_displayer is a private member populated using MEF
//[ImportMany(typeof (IDisplayer))]
//private IEnumerable<IDisplayer> _displayers;
DisplayTypeComboBox.ItemsSource = _displayers;
The ComboBox Xaml:
<ComboBox
Name="DisplayTypeComboBox"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="1"
IsEditable="False"
SelectionChanged="DisplayTypeComboBox_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<ComboBoxItem Content="{Binding DisplayerName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The IDisplayer:
public interface IDisplayer
{
string DisplayDataLocation { get; set; }
string DisplayerName { get; }
string DisplayerDescription { get;}
bool WatcherEnabled { get; }
UserControl View { get; }
string DisplayerImageLeft { get; set; }
string DisplayerImageRight { get; set; }
void Update();
}
I don't even want to think about how many hours I have spent trying to solve what should be a simple problem. Why is it so hard to get your selected text to appear as the selected value? I give up, WPF you have beat me into submission. I changed the control to a list box it takes up more room to display the selectable Items but at least it works.
<ListBox
Name="DisplayTypeComboBox"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="1"
SelectionChanged="DisplayType_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayerName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I encountered the same thing. Took me a while too. :(
You should have used the ItemContainerStyle and not ItemTemplate.
Because ComboBox wraps the internal items with a ComboBoxItem - you basically wrapped the ComboBoxItem with another one.
Check what DisplayerName member actually contains. Most likely it contains the UserControl name instead of the Display name.
Try using a TextBlock to bind to the DisplayerName instead of a ComboboxItem. I believe that when you set the itemsource, the combo control will wrap the items inside comboboxitems controls automatically.
Edit: I misunderstood your question. Try setting the SelectionBoxItemTemplate.