1) Copy and paste the following code into MainWindow.xaml file.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<Style x:Key="ContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="MediumPurple" BorderThickness="1" BorderBrush="MediumPurple" CornerRadius="5">
<StackPanel>
<Button Margin="3,3,3,3" Height="26" Command="ApplicationCommands.Cut">
<DockPanel HorizontalAlignment="Left" Width="150">
<Image x:Name="Image1" Height="20" Width="30"/>
<TextBlock Margin="5,2,5,0" FontSize="16" FontWeight="Bold" FontFamily="Wingdings 2" Text="&"/>
<TextBlock VerticalAlignment="Center" FontWeight="Bold" Text="Cut"/>
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Margin="0,0,5,0" Text="Ctrl+X"/>
</DockPanel>
</Button>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TextBox Height="50" Width="300">
<TextBox.ContextMenu>
<ContextMenu Style="{StaticResource ContextMenuStyle}"/>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window>
2) Copy and paste the following code into MainWindow.xaml.cs file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Image1.Source = BitmapFrame.Create(new System.IO.MemoryStream(System.Convert.FromBase64String("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")), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
}
This is the error message:
The name 'Image1' does not exist in the current context
This is the error picture:
So, how to access Image1 element from xaml control template?
Templates (ControlTemplate, DataTemplate) are expected to be used to create multiple copies of the same layout. Those copies may be parametrized via Bindings or resources.
To set Source for Image inside template, you can use
1) a DynamicResource
changes in xaml:
<Image x:Name="Image1" Height="20" Width="30" Source="{DynamicResource MyPicture}"/>
code-behind:
public partial class MainWindow: Window
{
public MainWindow()
{
var img = BitmapFrame.Create(new System.IO.MemoryStream(System.Convert.FromBase64String("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")), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
this.Resources["MyPicture"] = img;
InitializeComponent();
}
}
2) Binding
changes in xaml:
<Image x:Name="Image1" Height="20" Width="30" Source="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
code-behind (txt is a Name assigned to TextBox: <TextBox Height="50" Width="300" x:Name="txt">)
public partial class MainWindow: Window
{
public MainWindow()
{
InitializeComponent();
var img = BitmapFrame.Create(new System.IO.MemoryStream(System.Convert.FromBase64String("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")), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
txt.Tag = img;
}
}
Using Tag is a quick-and-dirty solution. When I need to custiomize templates, I prefer to create specialized Attached Dependency Properties - see this example for details
if the real problem is "how to set Image.Source from base64 string", then it is possible to do with a value converter, without code behind:
converter:
public class Base64ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string)
{
return BitmapFrame.Create(new System.IO.MemoryStream(System.Convert.FromBase64String((string)value)), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
template:
<Window.Resources>
<local:Base64ImageConverter x:Key="imageConverter"/>
<system:String x:Key="imageLetter">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</system:String>
<Style x:Key="ContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border Background="MediumPurple" BorderThickness="1" BorderBrush="MediumPurple" CornerRadius="5">
<StackPanel>
<Button Margin="3,3,3,3" Height="26" Command="ApplicationCommands.Cut">
<DockPanel HorizontalAlignment="Left" Width="150">
<Image x:Name="Image1" Height="20" Width="30" Source="{Binding Source={StaticResource imageLetter}, Converter={StaticResource imageConverter}}"/>
<TextBlock Margin="5,2,5,0" FontSize="16" FontWeight="Bold" FontFamily="Wingdings 2" Text="&"/>
<TextBlock VerticalAlignment="Center" FontWeight="Bold" Text="Cut"/>
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Margin="0,0,5,0" Text="Ctrl+X"/>
</DockPanel>
</Button>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Source is set like this (using converter and base64 string, both defined in Resources):
Source="{Binding Source={StaticResource imageLetter}, Converter={StaticResource imageConverter}}"
Related
I've got one page in my WPF app that should display some "tiles" in number as I specify before. Tile looks like this:
So my page should look something like this:
It is achievable of course by manually cloning tiles, but I want to avoid this (achieve it in more programmatic way). So instead of creating 6 clones I should stick to only one and then if needed add remaining ones. How can I accomplish that? I guess I should create my own UserControl like this:
<Grid HorizontalAlignment="Left" Height="199" VerticalAlignment="Top" Width="207" Background="Black">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="0*"/>
</Grid.RowDefinitions>
<Image x:Name="image1" HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="207" Stretch="UniformToFill"/>
<Grid HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="112" Background="#FFC78D10">
<TextBox IsReadOnly = "True" x:Name="CategoryOfEvent" Height="30" TextWrapping="Wrap" Text="Category" Width="112" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="18" SelectionBrush="{x:Null}" HorizontalAlignment="Left" VerticalAlignment="Top" >
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
</Grid>
<TextBox IsReadOnly = "True" x:Name="HourOfEvent" HorizontalAlignment="Left" Height="28" Margin="0,42,0,0" TextWrapping="Wrap" Text="Hour" VerticalAlignment="Top" Width="148" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFE2E2E2" FontSize="22" SelectionBrush="{x:Null}" FontWeight="Bold" TextChanged="HourOfEvent_TextChanged">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
<TextBox IsReadOnly = "True" x:Name="TitleOfEvent" HorizontalAlignment="Left" Height="88" Margin="0,82,0,0" TextWrapping="Wrap" Text="Title" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="20" SelectionBrush="{x:Null}" FontWeight="Bold">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
<TextBox IsReadOnly = "True" x:Name="PlaceOfEvent" HorizontalAlignment="Left" Height="24" Margin="0,175,0,0" TextWrapping="Wrap" Text="Where" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="14" SelectionBrush="{x:Null}">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
</Grid>
and just add them to my page. I would like also to mention that in every tiles there are 4 textboxes which are displaying some data parsed from Json, so maybe some automatic binding should do the job?
It is as simple as that.Firstly,what you can do is,create a UserControl with all your controls inside like TextBlocks and others.Then,decide which type of container control you want to use to hold your UserControl.Let's assume it's a grid.You can specify/set grid's column/rows for each user control.A sample :
private void addControl()
{
UserControl1 MyCon = new UserControl1;
MyGrid.Children.Add(MyCon);
Grid.SetRow(MyCon , 1); ////replace 1 with required row count
}
You can create grid rows in design time,or u can do it in code behind as well :
MyGrid.RowDefinitions.Add(new RowDefinition);
If you want to use columns instead,just apply same code but change Row/Rowdefinition with Column/ColumnDefinition
Hope this helps :)
The follwing example shows how to create multiple of the tiles you have been posting using a DataTemplate and WrapPanel. The DataTemplate specifies how an object (in this case a TileItem) is visualized. You can create multiple TileItems and then add them to an collection, in order to visualize them all.
Assuming your UI resides in MainWindow, you can create a collection with three items in it.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TileItemCollection = new ObservableCollection<TileItem>(new []
{
new TileItem(){Category = "Alpha", Hour = "10", Title = "Hello World", Where = "Office"},
new TileItem(){Category = "Beta", Hour = "15", Title = "Test", Where = "Home"},
new TileItem(){Category = "Gamma", Hour = "44", Title = "My Title", Where = "Work"},
});
DataContext = this;
}
public ObservableCollection<TileItem> TileItemCollection { get; }
}
You could load your Items from JSON and create an TileItem for each one in the JSON document. The class for TileItemss can be found below.
public class TileItem : INotifyPropertyChanged
{
private string _hour;
private string _title;
private string _where;
private string _category;
public string Category
{
get => _category;
set
{
if (value == _category) return;
_category = value;
OnPropertyChanged();
}
}
public string Hour
{
get => _hour;
set
{
if (value == _hour) return;
_hour = value;
OnPropertyChanged();
}
}
public string Title
{
get => _title;
set
{
if (value == _title) return;
_title = value;
OnPropertyChanged();
}
}
public string Where
{
get => _where;
set
{
if (value == _where) return;
_where = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note that in order for datachanges to be propagated to the UI, all properties which should be updated in the UI when you update them in code need to raise the property changed event. In this example all properties do this by default.
You can then update the XAML to bind to a collection. The ItemsControl acts as a container for the tiles. If you scroll down further you may notice the use of WrapPanel which is responsible for the item wrapping effect when you resize the control.
<ItemsControl ItemsSource="{Binding TileItemCollection}" Margin="20">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:TileItem}" >
<Grid HorizontalAlignment="Left" Height="199" VerticalAlignment="Top" Width="207" Background="Black">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="0*"/>
</Grid.RowDefinitions>
<Image x:Name="image1" HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="207" Stretch="UniformToFill"/>
<Grid HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="112" Background="#FFC78D10">
<TextBox IsReadOnly="True" Height="30" TextWrapping="Wrap" Text="{Binding Path=Category}" Width="112" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="18" SelectionBrush="{x:Null}" HorizontalAlignment="Left" VerticalAlignment="Top" >
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
</Grid>
<TextBox IsReadOnly="True" HorizontalAlignment="Left" Height="28" Margin="0,42,0,0" TextWrapping="Wrap" Text="{Binding Path=Hour}" VerticalAlignment="Top" Width="148" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFE2E2E2" FontSize="22" SelectionBrush="{x:Null}" FontWeight="Bold">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
<TextBox IsReadOnly="True" HorizontalAlignment="Left" Height="88" Margin="0,82,0,0" TextWrapping="Wrap" Text="{Binding Path=Title}" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="20" SelectionBrush="{x:Null}" FontWeight="Bold">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
<TextBox IsReadOnly="True" x:Name="PlaceOfEvent" HorizontalAlignment="Left" Height="24" Margin="0,175,0,0" TextWrapping="Wrap" Text="{Binding Path=Where}" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="14" SelectionBrush="{x:Null}">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer Name="PART_ContentHost"/>
</ControlTemplate>
</TextBox.Template>
</TextBox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
Each Tile is bound to an TileItem which means that the Bindings which point to e.g. Category, point to the Category of an TileItem.
To increase reusability it would be possible to move the code into its own usercontrol and optionally to add DependencyPropertys for better control.
I have custom range slider which works fine but I would like to change the colour of the text boxes, here is my XAML:
<UserControl x:Class="Slider.RangeSlider"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Slider"
mc:Ignorable="d"
x:Name="root"
>
<UserControl.Resources>
<ControlTemplate x:Key="simpleSlider" TargetType="{x:Type Slider}">
<Border SnapsToDevicePixels="true"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="PART_SelectionRange"/>
<Track x:Name="PART_Track" Grid.Row="1">
<Track.Thumb>
<Thumb x:Name="Thumb">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Rectangle Fill="#C0C0C0"
Stroke="Black"
StrokeThickness="1"
Width="10"
Height="18"
SnapsToDevicePixels="True"/>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderThickness="0,0,0,0"
BorderBrush="White"
VerticalAlignment="Center"
Height="1"
Margin="5,0,5,0"/>
<Rectangle Grid.Row="0" VerticalAlignment="Center" Fill="#FFF78A09" Height="5" MaxHeight="5" Margin="10,0,20,0"/>
<Slider x:Name="LowerSlider" Minimum="1" Grid.Row="1"
Maximum="12"
Value="{Binding LowerValue, ElementName=root}" IsSnapToTickEnabled="True" TickPlacement="Both"
Ticks="1,2,3,4,5,6,7,8,9,10,11,12" Template="{StaticResource simpleSlider}"
Margin="10,0,20,0"/>
<Slider x:Name="UpperSlider" Grid.Row="0" Thumb.DragCompleted="Thumb_Complete"
Minimum="1"
Maximum="12"
Value="{Binding UpperValue, ElementName=root}" IsSnapToTickEnabled="True" TickPlacement="Both"
Ticks="1,2,3,4,5,6,7,8,9 10,11,12" Template="{StaticResource simpleSlider}"
Margin="10,0,20,0"/>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<TextBox Name="Month1" Text="Jan" Width="30" Margin="1.6,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
<TextBox Name="Month2" Text="Feb" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
<TextBox Name="Month3" Text="Mar" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
<TextBox Name="Month4" Text="Apr" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}"/>
<TextBox Name="Month5" Text="May" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}"/>
<TextBox Name="Month6" Text="Jun" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
<TextBox Name="Month7" Text="Jul" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}"/>
<TextBox Name="Month8" Text="Aug" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}"/>
<TextBox Name="Month9" Text="Sep" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
<TextBox Name="Month10" Text="Oct" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}"/>
<TextBox Name="Month11" Text="Nov" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
<TextBox Name="Month12" Text="Dec" Width="30" Margin="2,0,0,0" Background="{x:Null}" Foreground="#FFF78A09" FontFamily="Tahoma" BorderBrush="#FFF78A09" IsReadOnly="True" SelectionBrush="{x:Null}" />
</StackPanel>
</Grid>
Here is the C# code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Slider
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class RangeSlider : UserControl
{
public RangeSlider()
{
InitializeComponent();
}
public RoutedEventHandler Custom_Complete;
private void Thumb_Complete(object sender,RoutedEventArgs e)
{
if (Custom_Complete !=null)
{
Custom_Complete(this, new RoutedEventArgs());
}
}
#region Setting dependancies
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
public double LowerValue
{
get { return (double)GetValue(LowerValueProperty); }
set { SetValue(LowerValueProperty, value); }
}
public static readonly DependencyProperty LowerValueProperty =
DependencyProperty.Register("LowerValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
public double UpperValue
{
get { return (double)GetValue(UpperValueProperty); }
set { SetValue(UpperValueProperty, value); }
}
public static readonly DependencyProperty UpperValueProperty =
DependencyProperty.Register("UpperValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(1d));
#endregion
}
}
What I want to do is to set an event for every time MouseUp happens, so that it checks the value of UpperSlider & LowerSlider and will change the border colour of each text box from Orange to Grey if the sliders are not in the range.
Can anyone help?
First, your RangeSlider deals with numeric ranges, so I don't think it makes sense to put month indicators inside the UserControl. I would split those out separately.
First, remove the StackPanel with the text boxes from the RangeSlider Xaml. Create a new DateRangePicker (or similarly named) user control, and add a RangeSlider and the original text boxes. Remove the duplicate property setters from the text boxes, and move them into a Style:
<UserControl x:Class="WpfTest2.DateRangePicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTest2">
<StackPanel>
<local:RangeSlider x:Name="rangeSlider"
Minimum="1" Maximum="12"
LowerValue="1" UpperValue="12" />
<UniformGrid Columns="12" Rows="1" Margin="8,5,20,0">
<UniformGrid.Resources>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="2,0,0,0" />
<Setter Property="FontFamily" Value="Tahoma" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Focusable" Value="False" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="SelectionBrush" Value="{x:Null}" />
<Setter Property="BorderBrush"
Value="{Binding RelativeSource={RelativeSource Self}, Path=Foreground}" />
<Setter Property="Foreground">
<Setter.Value>
<MultiBinding Mode="OneWay">
<MultiBinding.Converter>
<local:InRangeConverter />
</MultiBinding.Converter>
<Binding ElementName="rangeSlider" Path="LowerValue" />
<Binding RelativeSource="{RelativeSource Self}" Path="Tag" />
<Binding ElementName="rangeSlider" Path="UpperValue" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</UniformGrid.Resources>
<TextBox Tag="1" Text="Jan" />
<TextBox Tag="2" Text="Feb" />
<TextBox Tag="3" Text="Mar" />
<TextBox Tag="4" Text="Apr" />
<TextBox Tag="5" Text="May" />
<TextBox Tag="6" Text="Jun" />
<TextBox Tag="7" Text="Jul" />
<TextBox Tag="8" Text="Aug" />
<TextBox Tag="9" Text="Sep" />
<TextBox Tag="10" Text="Oct" />
<TextBox Tag="11" Text="Nov" />
<TextBox Tag="12" Text="Dec" />
</UniformGrid>
</StackPanel>
</UserControl>
(Note: If you decide you don't want to split up the date labels from the range slider, just replace the original StackPanel with the UniformGrid from above, and update the bindings to say ElementName="root" instead of ElementName="rangeSlider".)
I used a MultiBinding to set the Foreground. It relies on this custom converter:
public class InRangeConverter : IMultiValueConverter
{
private static readonly SolidColorBrush SelectedBrush =
new SolidColorBrush(Color.FromArgb(0xFF, 0xF7, 0x8A, 0x09));
private static readonly SolidColorBrush UnselectedBrush =
new SolidColorBrush(Color.FromArgb(0x3F, 0xF7, 0x8A, 0x09));
private static readonly object[] ConvertBackResult =
{ DependencyProperty.UnsetValue };
public object Convert(object[] values, Type type, object p, CultureInfo c)
{
if (values?.Length == 3)
{
var lower = values[0] as double?;
var value = values[1] as double?;
var upper = values[2] as double?;
if (value == null)
{
var stringValue = values[1]?.ToString();
if (stringValue != null && double.TryParse(stringValue, out var d))
value = d;
}
if (value >= lower && value <= upper)
return SelectedBrush;
return UnselectedBrush;
}
return Brushes.Gray;
}
public object[] ConvertBack(object value, Type[] types, object p, CultureInfo c)
{
return ConvertBackResult;
}
}
You weren't clear on how you wanted to adjust the colors, so I change the color based on whether the date is in the selected range. You can adjust the logic and the colors as necessary.
Results:
NOTE: Right now, you can set an upper value that's less than the lower value. You should fix that.
I'm making an application using Caliburn.Micro(for easy data binding and stuff) and MahApps.Metro(for designing).
I've created a View name 'MainView' which has HamburgerMenu of MahApps.
My issue is data binding is working fine under HamburgerMenu.ContentTemplate tag
Here is my HamburgerMenu.ContentTemplate xaml.
<Page x:Class="Sample.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:utils="clr-namespace:Omni.WindowsClient.Utils"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Omni.WindowsClient.Views"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="600">
<Page.Resources>
<DataTemplate x:Key="HamburgerMenuItem"
DataType="{x:Type mah:HamburgerMenuItem}">
<Grid Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Glyph}"
Stretch="UniformToFill" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Label}" />
</Grid>
</DataTemplate>
</Page.Resources>
<Grid>
<mah:HamburgerMenu x:Name="HamburgerMenuControl"
SelectedIndex="0"
ItemTemplate="{StaticResource HamburgerMenuItem}"
OptionsItemTemplate="{StaticResource HamburgerMenuItem}"
IsPaneOpen="True"
DisplayMode="CompactInline"
cal:Message.Attach="[Event ItemClick] = [Action ShowDetails(HamburgerMenuControl.SelectedItem)]"
DataContext="{Binding RelativeSource={RelativeSource self}}">
<mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenuItemCollection>
<mah:HamburgerMenuItem Label="System Status">
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Tasks" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
<mah:HamburgerMenuItem Label="Inbox">
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Inbox" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Certificate" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
</mah:HamburgerMenuItemCollection>
</mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenu.ContentTemplate>
<DataTemplate DataType="{x:Type mah:HamburgerMenuItem}">
<Grid utils:GridUtils.RowDefinitions="48,*">
<!--cal:Action.TargetWithoutContext="{Binding ElementName=HamburgerMenuControl, Path=DataContext}"-->
<Border Grid.Row="0"
Background="{DynamicResource MahApps.Metro.HamburgerMenu.PaneBackgroundBrush}">
<TextBlock x:Name="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Foreground="White" />
<!--Text="{Binding Path=Header, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"-->
</Border>
<Frame Grid.Row="1"
cal:Message.Attach="RegisterFrame($source)"
DataContext="{x:Null}"
NavigationUIVisibility="Hidden" />
</Grid>
</DataTemplate>
</mah:HamburgerMenu.ContentTemplate>
</mah:HamburgerMenu>
</Grid>
</Page>
and respective view model code is:
using Caliburn.Micro;
using MahApps.Metro.Controls;
using System.Windows.Controls;
namespace Sample.ViewModels
{
public class MainViewModel : Screen
{
private readonly SimpleContainer _container;
private INavigationService _navigationService;
private string _header;
public string HeaderTitle
{
get { return _header; }
set
{
_header = value;
NotifyOfPropertyChange();
}
}
public MainViewModel(SimpleContainer container)
{
this._container = container;
DisplayName = "Main";
}
public void RegisterFrame(Frame frame)
{
_navigationService = new FrameAdapter(frame);
_container.Instance(_navigationService);
_navigationService.NavigateToViewModel(typeof(SystemStatusViewModel));
HeaderTitle = "System Status";
}
public void ShowDetails(HamburgerMenuItem menuItem)
{
switch (menuItem.Label)
{
case "System Status":
_navigationService.NavigateToViewModel(typeof(SystemStatusViewModel));
HeaderTitle = "System Status";
break;
case "Inbox":
_navigationService.NavigateToViewModel(typeof(InboxViewModel));
HeaderTitle = "Inbox";
break;
default:
break;
}
}
}
}
I want to change View in frame under HamburgerMenu.ContentTemplate when I click on menu item.
Like System Status view is SystemStatusView
and Inbox view is InboxView.
My code is working fine (it changes the view in frame and change the Header label too) if I don't use HamburgerMenu.ContentTemplate. But I want to use HamburgerMenu.ContentTemplate to work with HamburgerMenu.
Thanks!
If it's working fine if you don't use HamburgerMenu.ContentTemplate, but stops working when you do, the problem is probably with you overwriting the default template in a way that doesn't support all functionalities of a control.
I'd suggest you to use Blend to get the default HamburgerMenu.ContentTemplate, then just edit it to your needs, without changing too much (keep in mind that names of controls used as a template may have a crucial meaning, so be careful what you are editing).
If you don't know how to use Blend to get your control's template, here is a simple tutorial described in a documentation of Telerik controls (don't worry, it works the same for all controls). You just need to create copy of a HamburgerMenu.ContentTemplate, paste it to your application and you are good to go (editing).
I used for the first time a custom Control (wpf). Here is what I want:
I want a Custom ListView with Custom ListViewItem (the challenge is I don’t know what’s on the “Form”, I just know it’s a Panel - I call "Form" what's on the ListViewItem ).
I have a CustomListView and a CustomListViewItem :
EDIT : CustomListView.cs
public class CustomListView : ItemsControl
{
public static readonly DependencyProperty ListContentProperty = DependencyProperty.Register("ListContent", typeof(IList), typeof(CustomListView), new PropertyMetadata());
public IList ListContent
{
get { return (IList)GetValue(ListContentProperty); }
set { SetValue(ListContentProperty, value); }
}
public CustomListView(ContentControl contentControl, IList list)
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomListView), new FrameworkPropertyMetadata(typeof(CustomListView)));
DataContext = list;
ItemsSource = list;
CustomListViewItem customListViewItem = new CustomListViewItem();
customListViewItem.Content = contentControl;
string xamlTemplate = XamlWriter.Save(customListViewItem);
string template =
"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
xamlTemplate +
"</DataTemplate>";
ItemTemplate = (DataTemplate)XamlReader.Parse(template);
SetValue(TemplateContentProperty, contentControl);
}
}
EDIT : CustomListViewItem .cs
public class CustomListViewItem : ContentControl
{
public static readonly DependencyProperty MyEventProperty = DependencyProperty.Register("MyEvent", typeof(EventHandler), typeof(CustomListViewItem));
public static readonly DependencyProperty ListContentItemProperty = DependencyProperty.Register("ListContentItem", typeof(IList), typeof(CustomListViewItem), new PropertyMetadata());
public IList ListContentItem
{
get { return (IList)GetValue(ListContentItemProperty); }
set { SetValue(ListContentItemProperty, value); }
}
public EventHandler MyEvent
{
get { return (EventHandler)GetValue(MyEventProperty); }
set { SetValue(MyEventProperty, value); }
}
public CustomListViewItem()
{
DefaultStyleKey = typeof(CustomListViewItem);
}
//Some method for the Event on my button
The DataContext of the CustomListView is an ObservableCollection. When I create the “Form” directly on my CustomListViewItem, everything is perfect. I can sort with my button, add a new empty Form…
Now here is my problem.
Let’s say we have a class Person (with only a name) who implements INotifyPropertyChanged. We create some “Person” and add them to an ObservableCollection. On a new xaml element, we create a Template (PersonEditor) the “Form”, with some Binding.
What I want on the MainWindows is something like that:
CustomListView customListView = new CustomListView(new PersonEditor(), _person);
myContentControl.Content = customListView;
Where _person is the ObservableCollection. We give the Template of the Form to the CustomListViewItem and add it to a content of a ContentControl
EDIT : Generics.xaml :
<Style x:Key="{x:Type customControl:CustomListView}" TargetType="customControl:CustomListView" BasedOn="{StaticResource {x:Type ItemsControl}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type customControl:CustomListView}">
<Border>
<ItemsPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type customControl:CustomListViewItem}" TargetType="customControl:CustomListViewItem" BasedOn="{StaticResource {x:Type ContentControl}}">
<Setter Property="ListContentItem">
<Setter.Value>
<Binding Path="ListContent" RelativeSource="{RelativeSource FindAncestor,AncestorType=customControl:CustomListView}"></Binding>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="customControl:CustomListViewItem">
<Border Background="White" x:Name="borderSelect" Padding="0" Width="Auto" Height="Auto">
<DockPanel Width="Auto" Height="Auto" VerticalAlignment="Stretch">
<ContentPresenter/>
<StackPanel Orientation="Horizontal">
<StackPanel SnapsToDevicePixels="true" Orientation="Vertical" Margin="0">
<StackPanel SnapsToDevicePixels="true" Orientation="Horizontal" Margin="0">
<Button SnapsToDevicePixels="true" Style="{StaticResource SquareCorner}" Opacity="0.0" Margin="0 7 0 0" Height="30" Width="12" VerticalAlignment="top" HorizontalAlignment="Right" Content="x" Padding="0" x:Name="DeleteButton" Command="{TemplateBinding MyEvent}"/>
<StackPanel SnapsToDevicePixels="true" Orientation="Vertical">
<Button Style="{StaticResource RoundUpCorner}" Opacity="0.0" Margin="0 7 0 0" Height="15" Width="15" VerticalAlignment="top" HorizontalAlignment="Right" Content="^" Padding="0 0 0 5" x:Name="UpButton" Command="{TemplateBinding MyEvent}"/>
<Button Style="{StaticResource RoundDownCorner}" Opacity="0.0" Height="15" Width="15" VerticalAlignment="top" HorizontalAlignment="Right" Content="v" Padding="0" x:Name="DownButton" Command="{TemplateBinding MyEvent}"/>
</StackPanel>
</StackPanel>
</StackPanel>
<DockPanel VerticalAlignment="Stretch">
<Button Style="{StaticResource SquareLittleCorner}" DockPanel.Dock="Top" Opacity="0.0" Height="15" Width="15" VerticalAlignment="top" HorizontalAlignment="Right" Margin="1 0 0 0" Content="+" Padding="0" x:Name="AddUpButton" Command="{TemplateBinding MyEvent}"/>
<Button Style="{StaticResource SquareLittleCorner}" DockPanel.Dock="Bottom" Opacity="0.0" Height="15" Width="15" VerticalAlignment="bottom" HorizontalAlignment="Right" Margin="1 0 0 0" Content="+" Padding="0" x:Name="AddButton" Command="{TemplateBinding MyEvent}"/>
</DockPanel>
</StackPanel>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The result is I only have one element that has the good Template, the other ones are created but empty.
I think the problem comes from the binding. But I don’t know what to do. I tried to create a new “Form” with xamlReader/xamlLoad (How can you clone a WPF object?) but with the binding again it’s not good. I also tried to clone the xaml but again, no binding and no event on button.
I don’t know what else I can do to solve this problem ? It’s my first time with a custom control so if I made a mistake (anything) just share please.
EDIT
I think my custom is better now, I follow the idea of the DataTemplate and ContentControl mix with xamlReader/xamlLoad. The Binding is good (same for Event). But, why when I used it before (like a Content not a Template) the Binding wasn't good ? And I don't know if xamlReader/xamlLoad is a good way to do that (I mean it looks more like some cheat).
Im quite new to silverlight and windows 7 phone development. And I'm not sure what I missed but apparantly I missed something because it's not working as intended.
My goal, is to show a list of creatures, with only their name and hitpoints. But the whole Text={Binding}-stuff apparently doesn't work. So I wonder if any of you guys could help me with this.
When i say it dosen't work, its because the data is in the creature list, but not in the page/textblocks - it shows the right amount of creatures, but just not the data.
XAML
<phone:PhoneApplicationPage
x:Class="RPG_Assistent.Pages.DamageTrackerPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!--<Button Content="Damage" Height="72" HorizontalAlignment="Left" Margin="0,618,0,0" Name="btnDamage" VerticalAlignment="Top" Width="160" Click="btnDamage_Click" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="158,618,0,0" Name="txtDamage" Text="" VerticalAlignment="Top" Width="286" KeyUp="NumericOnlyTextBox_KeyUp"></TextBox>-->
<ListBox ItemsSource="{Binding creatureList}" Height="500" HorizontalAlignment="Center" Margin="6,6,0,0" Name="listBox1" VerticalAlignment="Top" Width="400">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Width="400" Height="120" >
<Button.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="80" Width="200">
<StackPanel Orientation="Vertical" Height="40">
<TextBlock Width="100" FontSize="22" Text="Name:" Height="40"/>
<TextBlock Width="100" Text="{Binding Name}" Height="40"/>
</StackPanel>
<StackPanel Orientation="Vertical" Height="40">
<TextBlock Width="100" FontSize="22" Text="Hitpoints:" Height="40"/>
<TextBlock Width="100" Text="{Binding HitPoints}" Height="40"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
<!--Sample code showing usage of ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="MenuItem 1"/>
<shell:ApplicationBarMenuItem Text="MenuItem 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
CS - called when page is done loading stuff in. ( called after InitializeComponent(); on my DamageTracker Page )
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
namespace RPG_Assistent.Pages
{
public partial class DamageTrackerPage : PhoneApplicationPage
{
List<Models.Creature> creatureList { get; set; }
public DamageTrackerPage()
{
InitializeComponent();
creatureList = new List<Models.Creature>();
#region ApplicationTitle Setup
ApplicationTitle.Text = Constants.AppName;
ApplicationTitle.TextAlignment = Constants.AppName_TextAlignment;
ApplicationTitle.FontSize = Constants.AppName_FontSize;
ApplicationTitle.FontWeight = Constants.AppName_FontWeight;
#endregion
//SetInputScope(txtDamage);
LoadCreatures();
DataContext = this;
}
public void LoadCreatures()
{
string name;
for (int i = 0; i < 10; i++)
{
name = "Monster " + i + 1;
creatureList.Add(new Models.Creature(name));
}
}
public void btnDamage_Click(object sender, RoutedEventArgs e)
{
}
#region textbox control - makes numeric only
private void SetInputScope(TextBox textBoxControl)
{
InputScopeNameValue digitsInputNameValue = InputScopeNameValue.TelephoneNumber;
textBoxControl.InputScope = new InputScope()
{
Names = {
new InputScopeName()
{
NameValue = digitsInputNameValue
}
}
};
}
private void MaskNumericInput(TextBox textBoxControl)
{
string[] invalidCharacters = { "*", "#", ",", "(", ")", "x", "-", "+", " ", "#", "." };
for (int i = 0; i < invalidCharacters.Length; i++)
{
textBoxControl.SelectionStart = textBoxControl.Text.Length;
}
}
private void NumericOnlyTextBox_KeyUp(object sender, KeyEventArgs e)
{
MaskNumericInput((TextBox)sender);
}
#endregion
}
}
CS - Creature class, is placed in "Models" folder - because i thought i would be clever
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace RPG_Assistent.Models
{
public class Creature
{
public string Name { get; set; }
public int HitPoints { get; set; }
public string Type { get; set; }
public Creature(string name)
{
this.Name = name;
this.HitPoints = 0;
this.Type = "Images/mob.jpg";
}
public void Damage(int damage)
{
HitPoints += damage;
}
public void Bloodied()
{
switch (this.Type)
{
case "Images/mob.jpg":
this.Type = "Images/mobhurt.jpg";
break;
case "Images/mobhurt.jpg":
this.Type = "Images/mob.jpg";
break;
}
}
}
}
Since you are binding to a list of Creatures, you do not need to put Creature.Name. You should be able to change it to Text={Binding Name} and Text={Binding Hitpoints}
It looks like it should be Text={Binding Name} or Text={Binding HitPoints}
EDIT: but, Text={Binding Path=Name} or Text={Binding Path=HitPoints} would work too.
EDIT 2: Sorry, I didn't notice your comment. I don't have VS in the computer, so I can't try it myself, but try setting the DataType on the DataTemplate to Creature.
Update your binding to the following. I´ve dropped Creature, from the binding path. Then it should work
<StackPanel Orientation="Vertical" Height="40">
<TextBlock Width="100" FontSize="22" Text="Name:" Height="40"/>
<TextBlock Width="100" Text="{Binding Path=Name}" Height="40"/>
</StackPanel>
<StackPanel Orientation="Vertical" Height="40">
<TextBlock Width="100" FontSize="22" Text="Hitpoints:" Height="40"/>
<TextBlock Width="100" Text="{Binding Path=HitPoints}" Height="40"/>
</StackPanel>
You always bind to the DataContext with direct bindings, and when setting the ItemsSource to a list, the DataContext becomes each item in the list for each row it will represent. So your thinking here is completely correct!
However: ContentControl act the same. When you set the Content of a ContentControl you basicly override the DataContext for the Content. The DataContext is thus set as your StackPanel, and it will render itself as your StackPanel, but you will also try to Bind to your StackPanel, and not to your Creature object anymore.
So you might want to do this:
Move your content StackPanel to a DataTemplate, set this DataTemplate as ContentTemplate on your Button and set the Content of the Button to a Binding of your Creature object, like so:
<Button Width="400" Height="120" Content="{Binding}">
<Button.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="80" Width="200">
<StackPanel Orientation="Vertical" Height="40">
<TextBlock Width="100" FontSize="22" Text="Name:" Height="40"/>
<TextBlock Width="100" Text="{Binding Path=Name}" Height="40"/>
</StackPanel>
<StackPanel Orientation="Vertical" Height="40">
<TextBlock Width="100" FontSize="22" Text="Hitpoints:" Height="40"/>
<TextBlock Width="100" Text="{Binding Path=HitPoints}" Height="40"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
My preferred way of handling these occasions I set up a collection for the view. It would look something like this
public class CreatureList : ObservableCollection<Creature>
{
// at least implement the constructor
}
After that you can use the new collection class in your window XAML definition.
<ResourceDictionary>
<local:CreatureList x:Key="creatures" />
</ResourceDictionary>
The definition of the local namespace has to be set to the assembly namespace where the class CreatureList would be found. After that you can use the defined list in your listbox definition.
<ListBox Name="creatureListBox" ItemsSource="{Binding Source={StaticResource creatures}}">
<!-- Template definition for each entry -->
</ListBox>
To use these objects in your window class, you have to set up some attributes and associate them to the specified entry.
public partial class DamageTrackerPage : PhoneApplicationPage
{
private readonly CreatureList creatureList;
}
In the constructor of the class you bind the attribute to the specified XAML definition.
public DamageTrackerPage() {
InitializeComponent();
creatureList = FindResource("creatures") as CreatureList;
}
Now when you add entries to the list or remove entries from it the changes will be updated to your window automatically.
This is at least the way I'm doing it in WPF, but I'm sure for WinPhone apps that should be the same.