I am working on a local project and I have some issues.
I want to create a template for some results that have 3 strings(where 1 is a hyperlink) and a picture and they come as an ObservableCollection of results type binded to ItemSource.
public TestClass {
public string Title { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public BitmapImage Thumbnail { get; set; }
}
So, I want to show those results in WPF and I want to use for each item a template and show them in a StackPanel (or ListView).
I tried with ListView but the only thing you can do is select the whole item, but I want also the link to be clickable.
My problem is: how can I create a template to use on each item and then add them in a list that 1 string is clickable?
As Unflux mentioned, that's a good way to do it. And as for the clickable link, use the Hyperlink control like I did below.
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding FirstName}" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="{Binding LastName}" Grid.Row="0" Grid.Column="1" />
<TextBlock Text="{Binding Age}" Grid.Row="0" Grid.Column="2" />
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3">
<Hyperlink NavigateUri="{Binding BlogAddress}" Click="Hyperlink_OnClick">
<TextBlock Text="{Binding BlogAddress}" />
</Hyperlink>
</TextBlock>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and code-behind
private void Hyperlink_OnClick(object sender, RoutedEventArgs e)
{
var link = sender as Hyperlink;
Process.Start(link.NavigateUri.ToString());
}
results in
You will probably want to style it a bit and maybe apply a different ItemsPanel to really customize the look of your collection. You can also decorate ItemsControl with scrolling.
Thanks with the idea of using ItemsControl with its template. But the Hyperlink I made it work with Click property and giving to it a:
public ICommand RunHyperlink {
get {
return new ActionCommand(this.ButtonClick);
}
}
private void ButtonClick() {
Process.Start(new ProcessStartInfo(this.Link));
}
Related
I am newbie in C# and started making a WPF application. My current application looks like this:
My goal is to produce a copy of the "month" combobox, "week" checkboxes, and "task" textbox everytime the Add field button was clicked. The groupbox should also adjust once new rows are created. On my XAML code, the said controls are inside a Grid called "control_grid".
<Grid x:Name="control_grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="78*" />
<ColumnDefinition Width="119*" />
<ColumnDefinition Width="178*" />
</Grid.ColumnDefinitions>
<ComboBox Name="month_select" Grid.Column="0" SelectedIndex="0" Grid.Row="0" BorderBrush="Aquamarine" BorderThickness="1" Margin="0,0,113.8,0.6" Grid.ColumnSpan="2" />
<GroupBox BorderThickness=".5" BorderBrush="DarkBlue" Grid.Column="1" Margin="10.4,0,24.8,0.6" >
<Grid Margin="0 4 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox x:Name="firstw" Grid.Column="0" />
<CheckBox x:Name="secw" Grid.Column="1" />
<CheckBox x:Name="thirdw" Grid.Column="2" />
<CheckBox x:Name="fourthw" Grid.Column="3" />
</Grid>
</GroupBox>
<TextBox x:Name="task_spec" Grid.Column="1" BorderBrush="DarkGray" BorderThickness="1" Margin="107.4,0,0.2,0.6" Grid.ColumnSpan="2" />
</Grid>
I have found a workaround from forums that creates the controls individually, but this is a tedious process and very inefficient. Is there any way to copy/clone the grid elements and add it dynamically on code behind? Thank you.
When you want to create controls dynamically you would always define a DataTemplate.
The optimal solution would implement MVVM. To keep it simple I implemented the properties in the code-behind file instead of implementing a view model.
The following example will add a new item when the button is clicked:
TaskItem.cs
// Model class that does the data of a task item.
//
// TODO::Implement INotifyPropertyChanged
// and raise PropertyChanged on property changed
class TaskItem : INotifyPropertyChanged
{
// TODO::Define an enumeration Month which contains a field for each month
public ObservableCollection<Month> Months { get; set; }
public Month SelectedMonth { get; set; }
public bool IsFirstWeekSelected { get; set; }
public bool IsSecondWeekSelected { get; set; }
public bool IsThirdWeekSelected { get; set; }
public bool IsFourthWeekSelected { get; set; }
public string TaskSummary { get; set; }
}
MainWindow.xaml.cs
partial class MainWindow
{
public ObservableCollection<TaskIten> TaskItems { get; set; }
public MainWindow()
{
this.DataContext = this;
this.TaskItems = new ObservableCollection<TaskIten>()
{
new TaskItem()
};
}
private void AddTaskItem_OnClick(object sender, RoutedEventArgs e)
{
this.TaskItems.Add(new TaskItem());
}
}
MainWindow.xaml
<Window>
<GroupBox>
<StackPanel>
<Button Click="AddTaskItem_OnClick"
Content="New Task" />
<ListBox ItemsSource="{Binding TaskItems}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{X:Type TaskItem}">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Months}"
SelectedItem="{Binding SelectedMonth}" />
<CheckBox IsChecked="{Binding IsFirstWeekSelected}" />
<CheckBox IsChecked="{Binding IsSecondWeekSelected}" />
<CheckBox IsChecked="{Binding IsThirdWeekSelected}" />
<CheckBox IsChecked="{Binding IsFourthWeekSelected}" />
<TextBox Text="{Binding TaskSummary}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</GroupBox>
</Window>
I'm trying to find a way to display a horizontal List of items in WPF, the trick is that the Window which contains the List will be displayed on various screen sizes and all the items in the list need to be resized to fill the available space without any use of scroll bars.
I've found that a ViewBox control can be used to achieve the desired affect, but the ViewBox works only if I set <RowDefinition Height="300"/>.This approach doesn't work because if you have a certain number of items in the List they start becoming cut off.
If I remove <RowDefinition Height="300"/> then the first item in the list fills the screen and the rest are cut off
Any suggestions on how I can make all the items in the list resize to fill the available space no matter what the screen resolution is?
XAML:
<Window x:Class="ViewBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowState="Maximized">
<ItemsControl ItemsSource="{Binding Path=Employees}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Grid.Column="0">
<TextBlock Text="{Binding Path=Name}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="1">
<TextBlock Text="{Binding Path=Surname}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="2">
<TextBlock Text="{Binding Path=Age}" />
</Viewbox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
C#:
using System.Collections.Generic;
using System.Windows;
namespace ViewBoxExample
{
public partial class MainWindow : Window
{
public List<Employee> _employees = new List<Employee>();
public List<Employee> Employees
{
get { return _employees; }
set { _employees = value; }
}
public MainWindow()
{
InitializeComponent();
Employees = new List<Employee>()
{
new Employee{ Name="Name1",Surname="Surname1",Age=20},
new Employee{ Name="Name2",Surname="Surname2",Age=30},
new Employee{ Name="Name3",Surname="Surname3",Age=40},
new Employee{ Name="Name4",Surname="Surname4",Age=50},
new Employee{ Name="Name5",Surname="Surname5",Age=60},
};
this.DataContext = this;
}
}
public class Employee
{
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
}
}
Just put your ItemsControl in ViewBox
<Viewbox>
<ItemsControl ItemsSource="{Binding Path=Employees}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Viewbox Grid.Row="0" Grid.Column="0">
<TextBlock Text="{Binding Path=Name}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="1">
<TextBlock Text="{Binding Path=Surname}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="2">
<TextBlock Text="{Binding Path=Age}" />
</Viewbox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Viewbox>
EDIT: if i am doing the same type of thing i would use the actual Height and Width approach as explained by Kent Boogaart in this Answer
I have a WPF application. It contains OrderBlock object which contains other objects, plesase see a brief view of the class.
public class OrderBlocks
{
private List<Order> _orders;
[XmlElement("tF_Transactions")]
public List<Order> Orders { get { return _orders; } set { _orders = value; OnPropertyChanged("Orders"); } }
}
public class Order : INotifyPropertyChanged
{
[XmlIgnore]
public List<Duplications> DuplicateHolder { get; set; }
}
public class Duplications
{
public string ID { get; set; }
public string Name { get; set; }
public Duplications(string newID, string newName)
{
ID = newID;
Name = newName;
}
}
I have a datagrid that is bound to my object Orders of type List Orders. My datagrid has a row detail so that when a user clicks on a row further details are displayed. I have added a listbox to this row detail. I want this row detail to show a listbox which displays my object DuplicateHolder of type List Duplications.
At the moment the listbox is empty. Please see my attempted XAML code below. Any help would be great as always.
<ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Name="lbIdentifier" SelectionMode="Single" DataContext="{Binding OrderBlock}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=DuplicateHolder.ID}" FontSize="10" HorizontalAlignment="Left" Margin="5,0,0,0"/>
<TextBlock Grid.Column="1" Text="{Binding Path=DuplicateHolder.Name}" FontSize="10" HorizontalAlignment="Left" Margin="5,0,0,0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Try this
<Listbox ItemSource = {Binding DuplicateHolder}/>
and
<TextBlock Grid.Column="0" Text="{Binding Path=ID}".../>
t seems like you did not set the bindings correctly because the listbox Context should be a list of Duplications and the ItemTemplate should be for one Duplication instance from the list of duplicates. So if the global datacontext is an instance of OrderBlocks the listbox will be bound to the DuplicateHolder of an Order:
<ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Name="lbIdentifier" SelectionMode="Single" DataContext="{Binding Path=DuplicateHolder}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=ID}" FontSize="10" HorizontalAlignment="Left" Margin="5,0,0,0"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Name}" FontSize="10" HorizontalAlignment="Left" Margin="5,0,0,0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am new to C#/XAML coding and I have the following question.
I have this ListView and I have added Aditional Templates to the generated Items
<ListView x:Name="lvItems" HorizontalAlignment="Left" Height="251" Margin="10,42,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Width="1346" SelectionChanged="lvItems_SelectionChanged" Foreground="{x:Null}" ItemTemplate="{StaticResource Standard500x130ItemTemplate}">
If I go to edit the template I get the following code
<!-- Grid-appropriate 500 by 130 pixel item template as seen in the GroupDetailPage -->
<DataTemplate x:Key="Standard500x130ItemTemplate">
<Grid Height="110"
Width="480"
Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"
Width="110"
Height="110">
<Image Source="{Binding Image}"
Stretch="UniformToFill"
AutomationProperties.Name="{Binding Title}" />
</Border>
<StackPanel Grid.Column="1"
VerticalAlignment="Top"
Margin="10,0,0,0">
<TextBlock Text="{Binding Title}"
Style="{StaticResource TitleTextStyle}"
TextWrapping="NoWrap" />
<TextBlock Text="{Binding Subtitle}"
Style="{StaticResource CaptionTextStyle}"
TextWrapping="NoWrap" />
<TextBlock Text="{Binding Description}"
Style="{StaticResource BodyTextStyle}"
MaxHeight="60" />
</StackPanel>
</Grid>
</DataTemplate>
Now I want to access the Texblocks Title, Subtitle, Description to add data that I have parsed from an XML File. I guess to to that I need to access the Binding of each TextBlock but I have no clue how to do that. Can you help me?
Thanks in advance for your help
You need to set the ItemsSource property of the ListView, and then the fields will populate for each item, based on the template you created.
lvItems.ItemsSource = MyObjectsCollection;
Here I'm assuming MyObjectCollection is a collection of your objects. Judging from your template, the data class should look something like this:
public class TheObject
{
public string Title { get; set; }
public string Subtitle { get; set; }
public string Description { get; set; }
public string Image { get; set; }
}
So MyObjectsCollection should be an array (or IEnumerable, or List) of TheObject objects.
I have the following XAML:
<Grid x:Name="ContentPanelDaily"
Grid.Row="1"
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
Style="{StaticResource PhoneTextAccentStyle}"
Margin="0,0,0,0">
First
</TextBlock>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource PhoneTextAccentStyle}"
Margin="0,0,0,0">
Second
</TextBlock>
</Grid>
How can i detect which row was clicked in Tap event? I have tried to find SelectedRow or something like this but it seems there is no anything like this in Grid. Thank you very much.
Put your data into a button, and subscribe to on OnClick event.
<Button OnClick="evetnHandler">
<TextBlock Grid.Row="0"
Grid.Column="0"
Style="{StaticResource PhoneTextAccentStyle}"
Margin="0,0,0,0">
First
</TextBlock>
</Button>
Also, as #Rachel said: you can use a ListBox.
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding Selected, Type=TwoWay}">
<ListBox.ItemTemplate>
<DataTemlate>
<TextBlock
Style="{StaticResource PhoneTextAccentStyle}"
Margin="0,0,0,0" Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in a code-behind create
public ObservableCollection<DataItem> Items {get;set;}
private DataItem _selected;
public DataItem Selected
{
get {return _selected;}
set
{
_selected = value;
//ha! item selected!!! handle it
}
}
public class DataItem
{
public string Name {get;set;}
}