I've looked online and found some topics related to my issue, although being new to XAML and WPF, i'm having trouble making what I want work.
I have a custom TimeLineControl StackPanel that contains 'Items' of type TimeLineFunctionControl, where the Items uses a DataTemplate to define how the 'Items' are displayed.
<!-- Static Resource = BgColor -->
<Color R="255" G="255" B="255" A="180" x:Key="BgColor" />
<!-- Static Resource = BgBrush -->
<SolidColorBrush Color="{DynamicResource BgColor}" x:Key="BgBrush" />
<!-- DataTemplate = TimeLineFunctionDataTemplate -->
<DataTemplate DataType="{x:Type tt:FunctionDataType}"
x:Key="TimeLineFunctionDataTemplate">
<Border x:Name="DataContainer"
BorderThickness="0.3"
BorderBrush="Black"
CornerRadius="2"
Margin="0,20,0,10"
Height="50"
Background="{DynamicResource BgBrush}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=StartTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
<TextBlock Text="{Binding Path=EndTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
</StackPanel>
</Border>
</DataTemplate>
public class FunctionDataType : ITimeLineDataItem
{
public TimeSpan? StartTime { get; set; }
public TimeSpan? EndTime { get; set; }
public Boolean TimelineViewExpanded { get; set; }
public String Name { get; set; }
}
I want to be able to change the Background color of the Border (DataContainer) dynamically from within the code.
I've tried the following;
1 - Doesn't work, I've since learnt that once a Template is applied, the Background property is no longer used.
titem.Background = (Brush)FindResource("BgBrushTriggered");
2 - Works, although I need to have defined two (2) DataTemplate in XAML, each with different Background colors, seems there must be a better way to do it.
titem.ContentTemplate = (DataTemplate)FindResource("TimeLineFunctionDataTriggeredTemplate");
3 - Works, although it changes ALL the items, since i'm changing the DynamicResource value.
this.Resources["BgBrush"] = new SolidColorBrush((Color)FindResource("BgColorTriggered"));
4 - Doesn't work, XAML reports "The member "Background" is not recognized or is not accessible";
Background="{TemplateBinding Background}"
Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}"
Q: What are my options?
Q: Is there a good online resource to correctly learn XAML and now to apply bindings, styles, templates etc...
The correct way to do this would be to bind the Background property of the Border to a property of the FunctionDataType objects and then set this property of the particular item you want to change.
You could either bind directly to a Brush property or define some other type of property and use a converter to convert this value into a Brush. The FunctionDataType must implement the INotifyPropertyChanged interface for this to work.
Please refer to the following sample code.
<DataTemplate DataType="{x:Type tt:FunctionDataType}"
x:Key="TimeLineFunctionDataTemplate">
<Border x:Name="DataContainer"
BorderThickness="0.3"
BorderBrush="Black"
CornerRadius="2"
Margin="0,20,0,10"
Height="50"
Background="{Binding BgBrush}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=StartTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
<TextBlock Text="{Binding Path=EndTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
</StackPanel>
</Border>
</DataTemplate>
public class FunctionDataType : ITimeLineDataItem, INotifyPropertyChanged
{
public TimeSpan? StartTime { get; set; }
public TimeSpan? EndTime { get; set; }
public Boolean TimelineViewExpanded { get; set; }
public String Name { get; set; }
private Brush _bgBrush;
public Brush BgBrush
{
get { return _bgBrush; }
set { _bgBrush = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
var item = titem.DataContext as FunctionDataType;
item.BgBrush = System.Windows.Media.Brushes.Red;
you can use a property in your model ItemColorIndex and then bind the background via a converter to it Background="{Binding ItemColorIndex , Converter=XXX}
Related
I have dxg:GridControl.
xaml:
<dxg:GridControl Name="DynamicGridControl"
ItemsSource="{Binding CommonEditCollection, Mode=TwoWay}"
SelectionMode="Cell"
AutoGenerateColumns="AddNew"
AutoGeneratedColumns="GridControl_AutoGeneratedColumns">
<dxmvvm:Interaction.Behaviors>
<lc:CellSelectionBehavior SelectedCells="{Binding SelectedCells, Mode=TwoWay}"/>
</dxmvvm:Interaction.Behaviors>
</dxg:GridControl>
ItemsSource binds to CommonEditCollection
viewmodel:
public ObservableCollection<Dictionary<int, DynamicTableModel>> CommonEditCollection { get; set; }
model:
public class DynamicTableModel
{
public double CellWidth { get; set; }
public string StrValue{ get; set; }
public bool IsBorerNull { get; set; }
public DynamicTableModel(string strVal, double cellWidth, bool isBorerNull = false)
{
StrValue = strVal;
CellWidth = cellWidth;
IsBorerNull = isBorerNull;
}
}
In xaml file I set Resources for cells style (I want to merge some cells):
<DataTemplate x:Key="CellDataTemplate">
<StackPanel>
<Border ...
</Border.Style>
</Border>
<dxg:CellEditor Content="{Binding Value.StrValue}"/>
</StackPanel>
</DataTemplate>
I bind CellEditor to property of DynamicTableModel class. But if I try edit text in any cell it throw NullReferenceException.
I cant change class DynamicTableModel to string because I need other properties. And I tried to use convertor Attribute, but it create new instance when I change text.
Help me please to change text in cells.
Project link: https://github.com/Kolgotin/DynamicGridControl
In result I just added this property:
<DataTemplate>
<dxe:TextEdit Name="PART_Editor" HorizontalContentAlignment="Stretch">
<dxe:TextEdit.EditTemplate>
<ControlTemplate>
<dxe:TextEdit x:Name="teNewValue"
HorizontalAlignment="Stretch"
EditValue="{Binding Value.StrValue}"/>
</ControlTemplate>
</dxe:TextEdit.EditTemplate>
</dxe:TextEdit>
</DataTemplate>
and handler works in "set" method
Self-taught programmer, would love any constructive criticism regarding my code.
I have a ListView that will have ListViewItems that I want to customize.
The ListViewItem I have made has two TextBlocks and a ToggleSwitch. When the ToggleSwitch is switched On/Off I want it to call a method from an instantiate object, or call a method from the same form, but somehow retrieve the object that initially loaded into the DataTemplate.
Here is the XAML so far:
<ListView x:Name="listViewAddedVideoFolders" Grid.Row="1" DoubleTapped="listViewAddedVideoFolders_DoubleTapped" SelectionChanged="listViewAddedVideoFolders_SelectionChanged" HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Center" Text="{Binding Directory}"/>
<Grid HorizontalAlignment="Right">
<StackPanel>
<TextBlock Text="Find Videos: "></TextBlock>
<ToggleSwitch Toggled="listViewVideoFolder_toggled" />
</StackPanel>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
Right now it is calling listViewVideoFolder_toggled
Before I was trying to use Toggled="{Binding StartCrawling()}"
Here is the AddVideoFolderModel object that I am binding the listviewitems to
namespace Movie_Management_Windows_10.Models
{
public class AddVideoFolderModel
{
public static ObservableCollection<AddVideoFolderModel> MyVideoFolderModels = new ObservableCollection<AddVideoFolderModel>();
public int VideosFound { get; set; }
public string Directory { get; set; }
public string DirectoryName { get; set; }
private bool isCrawling = false;
public bool HasBeenCrawled = false;
private void startCrawling()
{
AppShell.Current.NotifyUser("Crawling began", AppShell.NotifyType.StatusMessage);
}
//public override string ToString()
//{
// return Directory + " (" + VideosFound.ToString() + ")";
//}
}
}
What must I implement to accomplish this?
At first, you can add property to your model and bind to IsOn property in ToggleSwitch using TwoWay mode binding. Is this case, your model must implement INotifyPropertyChanged
private bool _isNeedCrawle;
public bool IsNeedCrawle
{
get
{
return _isNeedCrawle;
}
set
{
if (_isNeedCrawle != value)
{
_isNeedCrawle = value;
if (_isNeedCrawle)
{
startCrawling();
}
NotifyPropretyChanged("IsNeedCrawle");
}
}
}
At second, you can use XAML Behavior SDK. In this case, you must Add reference to library (look how to do it), and change method modifier from private to public
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
<ToggleSwitch>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Toggled">
<core:CallMethodAction MethodName="StartCrawling" TargetObject="{Binding }"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</ToggleSwitch>
I have a gridview that looks something like this:
<GridView ItemContainerStyle="{StaticResource GridViewItemStyle2}" ItemsSource="{Binding MyMeetingsSquareUsers}" Grid.Row="1" Margin="10,10,10,0" SelectionMode="None" HorizontalContentAlignment="Left" VerticalContentAlignment="Bottom">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Vertical" MaximumRowsOrColumns="1"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="35" Width="35" Margin="0,0,10,0" >
<Border BorderBrush="red" BorderThickness="1" CornerRadius="15">
<Ellipse>
<Ellipse.Fill>
<ImageBrush Stretch="Fill" ImageSource="ms-appx:///Images/photo_empty.png"/>
</Ellipse.Fill>
</Ellipse>
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The ItemsSource I use is a list of items of this type:
public class MeetingInvitee
{
public string id { get; set; }
public string status { get; set; }
public User user { get; set; }
public BitmapImage photo { get; set; }
}
What I would like to know is, if it is possible to change the color of the Border I use depending on the value in status
For example if I had 3 possible status: Accepted, Rejected, Pending, the colors would be set to either Green, Red or Yellow accordingly.
so if one of the items on the list had a status of Rejected the border would have a red brush
Conditional templates (instead of style data triggers unfortunately) are the way to go in a store app. What you need to do is define three different templates for each color and in the code behind create a template selector.
Data Template Selector
public class MeetingTemplateSelector : DataTemplateSelector
{
public DataTemplate AcceptedTemplate { get; set; }
public DataTemplate RejectedTemplate { get; set; }
public DataTemplate PendingTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item,
DependencyObject container)
{
DataTemplate result;
switch( ((MeetingInvitee) item).Status)
{
case "Accepted" : result = AcceptedTemplate; break;
case "Rejected" : result = RejectedTemplate; break;
case "Pending" : result = PendingTemplate; break;
}
return result;
}
}
Declare Templates in the Resources
<UserControl.Resources>
<DataTemplate x:Key="AcceptedTemplate">
<Border Background="Green">
...
</Border>
</DataTemplate>
<DataTemplate x:Key="RejectedTemplate">
<Border Background="Red">
...
</Border>
</DataTemplate>
<DataTemplate x:Key="PendingTemplate">
<Border Background="Yellow">
...
</Border>
</DataTemplate>
<local:MeetingTemplateSelector x:Key="meetingSelector"
AcceptedTemplate="{StaticResource AcceptedTemplate}"
RejectedTemplate="{StaticResource RejectedTemplate}"
PendingTemplate="{StaticResource PendingTemplate}">
</local:MeetingTemplateSelector >
</UserControl.Resources>
Usage
<GridView ItemContainerStyle="{StaticResource GridViewItemStyle2}"
ItemsSource="{Binding MyMeetingsSquareUsers}"
ItemTemplateSelector="{StaticResource meetingSelector}">
You can use an implementation of the IValueConverter interface and pass that when you bind the Background property of your Border to your Status property of the viewmodel. Example:
class StatusValueConverter : IValueConverter
{
private static SolidColorBrush _acceptedBrush = new SolidColorBrush(Colors.Green);
private static SolidColorBrush _pendingBrush = new SolidColorBrush(Colors.Yellow);
private static SolidColorBrush _rejectedBrush = new SolidColorBrush(Colors.Red);
public object Convert(object value, Type targetType, object parameter, string language)
{
SolidColorBrush brush = null;
if (value != null)
{
string status = value.ToString();
switch (status)
{
case "Accepted":
brush = _acceptedBrush;
break;
case "Pending":
brush = _pendingBrush;
break;
case "Rejected":
brush = _rejectedBrush;
break;
}
}
if (brush == null)
{
throw new ArgumentException("Status not valid.");
}
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
// You need this if TwoWay binding mode is used.
}
}
Add the value converter to the page resources (where local is the prefix for the namespace in which I defined the StatusValueConverter):
<Page.Resources>
<local:StatusValueConverter x:Key="StatusConverter" />
</Page.Resources>
In your xaml define your Border element like this:
<Border BorderBrush="{Binding Status, Converter={StaticResource StatusConverter}}"
BorderThickness="1" CornerRadius="15">
<Ellipse>
<Ellipse.Fill>
...
</Ellipse.Fill>
</Ellipse>
</Border>
If you want a completely different visual tree for different values in your view model, then use a DataTemplateSelector.
I am a newbie in templating Wpf controls. I use VS2013, WPF 4.5 and Caliburn Micro 2.0.2. In part of tasks I have I need to populate a grid with toggle buttons contained different images and its subtitle. I have solved it using UniformGrid. See my code below. They work but still don't have event and property binding since I don't know how I can bind the events and properties of toggle buttons to view model, since they are generated automatically and dynamically and the number of toggle buttons is uncertain (depends on the number of images in the image folder).
For example:
manually I could bind the Click event, IsChecked property and some other properties of toggle button 1 like following:
<ToggleButton x:Name="ToggleVehicle01" IsChecked={Binding SelectedVehicle01} Background="{Binding BackColorSelectedVehicle01}" ToolTip="{Binding VehicleName01}">
But now I can't do that anymore since the toggle buttons are generated automatically and their number is uncertain. Please help. Feel free to change my code below or give me examples code that works. Thank you in advance.
The View (MainView.xaml):
<UserControl x:Class="CMWpf02.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Width="1024"
Height="768"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ShowGridLines="True">
<ItemsControl Name="ImageList"
Background="#FFFFFFFF"
BorderBrush="#FFA90606"
ItemsSource="{Binding Path=VehicleImages}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Margin="0,0,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Width="180"
Margin="10,10,10,10"
FontSize="10"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}">
<!-- x:Name="ToggleVehicle01" -->
<!-- Background="{Binding BackColorSelectedVehicle01}" -->
<!-- IsChecked="{Binding SelectedVehicle01}" -->
<!-- ToolTip="{Binding Vehicle01Name}"> -->
<StackPanel Margin="0,5,0,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Width="140"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Path=Image}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=Name}" />
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The ViewModel (MainViewModel.cs):
using Caliburn.Micro;
using System;
using System.Collections.ObjectModel;
using System.IO;
namespace CMWpf02.ViewModels
{
public class MainViewModel : Screen, IHaveDisplayName
{
private String _path2Images = #"D:\tmp\Images";
public string DisplayName { get; set; }
public ObservableCollection<VehicleImage> VehicleImages { get; set; }
public MainViewModel()
{
DisplayName = "Main Window";
var vehicles = new ObservableCollection<String>();
vehicles = GetAllFilesFromFolder(_path2Images);
VehicleImages = new ObservableCollection<VehicleImage>();
foreach (var i in vehicles)
VehicleImages.Add(new VehicleImage(i));
}
public ObservableCollection<String> GetAllFilesFromFolder(String fullPathFolder)
{
string[] fileArray = Directory.GetFiles(fullPathFolder);
return new ObservableCollection<String>(fileArray);
}
}
public class VehicleImage
{
public String Image { get; private set; }
public String Name { get; private set; }
public VehicleImage(String image)
{
Image = image;
Name = Path.GetFileName(image);
}
}
//public void ToggleVehicle01()
//{
// var selectText = (SelectedVehicle01) ? " selected" : " unselected";
// MessageBox.Show(Vehicle01Name + selectText);
// BackColorSelectedVehicle01 = (SelectedVehicle01) ? _backColorSelectedVehicle : _defaultBackColorVehicle;
//}
//public Boolean SelectedVehicle02
//{
// get { return _selectedVehicle02; }
// set
// {
// _selectedVehicle02 = value;
// NotifyOfPropertyChange(() => SelectedVehicle02);
// }
//}
//public Brush BackColorSelectedVehicle02
//{
// get { return _backColorSelectedVehicle02; }
// set
// {
// _backColorSelectedVehicle02 = value;
// NotifyOfPropertyChange(() => BackColorSelectedVehicle02);
// }
//public String Vehicle01Name { get; private set; }
}
EDIT: Now I can bind the properties of generated ToggleButton with view model. I make the VehicleImage class to a view model (see modified code below). But I still have problem to bind Click-event of generated ToggleButton to view model.
The modified class to view model
public class VehicleImage : PropertyChangedBase
{
public String Image { get; private set; }
public String Name { get; private set; }
private Boolean _selectedVehicle;
public Boolean SelectedVehicle
{
get { return _selectedVehicle; }
set
{
_selectedVehicle = value;
BackColorSelectedVehicle = _selectedVehicle ? new SolidColorBrush(Color.FromArgb(255, 242, 103, 33)) : new SolidColorBrush(Colors.White);
}
}
private Brush _backColorSelectedVehicle;
public Brush BackColorSelectedVehicle
{
get { return _backColorSelectedVehicle; }
set
{
_backColorSelectedVehicle = value;
NotifyOfPropertyChange(() => BackColorSelectedVehicle);
}
}
// ToggleButton's Click-Event Handler, but it doesn't get event trigger from View.
// Therefore I set the BackColorSelectedVehicle fin setter of SelectedVehicle property.
public void ToggleSelection()
{
//BackColorSelectedVehicle = SelectedVehicle ? new SolidColorBrush(Color.FromArgb(255, 242, 103, 33)) : new SolidColorBrush(Colors.White);
}
public VehicleImage(String image)
{
Image = image;
Name = Path.GetFileName(image);
}
}
The modified view
<ToggleButton Width="180"
Margin="10,10,10,10"
Background="{Binding Path=BackColorSelectedVehicle}"
FontSize="10"
IsChecked="{Binding Path=SelectedVehicle}"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}"
ToolTip="{Binding Path=Name}">
<!-- x:Name="ToggleSelection" -->
<StackPanel Margin="0,5,0,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Width="140"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Path=Image}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=Name}" />
</StackPanel>
</ToggleButton>
In WP8 app, i have few controls where i bind the foreground color which i am changing in the codebehind. But OnPropertyChanged is not firing when the user event happened.
I have defined this binding "ControlForeground" in my textblock and radiobutton data template controls in it. I am trying to change the Foreground color whenever user presses the button. But my new color assignment is not updating the UI. Anything i am missing here?
In XAML,
<TextBlock x:Name="lblTileColor" TextWrapping="Wrap" Text="Selected color:" Foreground="{Binding ControlForeground, Mode=TwoWay}"/>
<TextBlock x:Name="lblTileColor2" TextWrapping="Wrap" Text="App bg:" Foreground="{Binding ControlForeground, Mode=TwoWay}"/>
<RadioButton x:Name="accentColor" IsChecked="true" BorderBrush="White" Foreground="{Binding ControlForeground, Mode=TwoWay}">
<RadioButton.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Width="25" Height="25" Fill="{StaticResource PhoneAccentBrush}"/>
<TextBlock Width="10"/>
<TextBlock x:Name="lblDefaultAccent" Text="Default accent color" Foreground="{Binding ControlForeground, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</RadioButton.ContentTemplate>
</RadioButton>
<Button x:name="UpdateColor" click="update_btn"/>
In C#,
public class ColorClass : INotifyPropertyChanged
{
private SolidColorBrush _ControlForeground;
public SolidColorBrush ControlForeground
{
get
{
return _ControlForeground;
}
set
{
_ControlForeground = value;
OnPropertyChanged("ControlForeground");
}
}
public ColorClass() { }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public class ColorPage:PhoneApplicationPage{
public ObservableCollection<ColorClass> TestCollection { get; private set; }
public void update_btn(object sender, EventArgs e){
TestCollection.Add(new ColorClass()
{
ControlForeground = new SolidColorBrush(Colors.Red)
});
}
}
For your 2nd problem (not being able to bind controls inside your data template), this is because these controls will use the data context of the their parent template not the data context of the page.
To fix this, you'll have to tell these controls the element name with the data context and give it full path of your property.
<TextBlock
x:Name="lblDefaultAccent"
Text="Default accent color"
Foreground="{Binding DataContext.ControlForeground,
ElementName=LayoutRoot, Mode=TwoWay}"/>
As you can see above you have to specify the element name. In case you bound this using this.DataContext = colorClass then the element name will be the name of the outer grid in your xaml, defaulted as LayoutRoot
You can only bind an ObservableCollection to controls which expect it, like a ListBox or LongListSelector. Additionally, adding a Brush to the TestCollection doesn't fire the non-functional notification since it doesn't call the setter of that property, just modifies the existing object.
Make TestCollection a type ColorClass and change the .Add stuff to just change the ColorClass.ControlForeground property and this should "just work."