I added a style in my StandardStyles.xaml that is defined like this:
<TextBlock Name="txtStatus" Text="{Binding Status}"
Margin="10,2,0,0" Width="350" TextTrimming="WordEllipsis"/>
The displayed text will then depend on the Status property on the bound data source. I would like to include the Word "Status:" before so that the final would be like this: "Status: Complete".
I would also like to have a conditional color depending on the status. In the above case, I would like Completed to be green (Status word would still be the normal color).
How can I do that?
For conditional styling you have to use data binding converter. First of all create a new class like given below.
public class StatusToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var _status = value.ToString();
if (_status == "To Do")
return new SolidColorBrush(Windows.UI.Colors.Red);
else if (_status == "In Progress")
return new SolidColorBrush(Windows.UI.Colors.Yellow);
else if (_status == "Completed")
return new SolidColorBrush(Windows.UI.Colors.Green);
else
return new SolidColorBrush(Windows.UI.Colors.White);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Now when you want to use, add it as page's resource like this
<Page.Resources>
<local:StatusToColorConverter x:Key="StatusToColor"/>
</Page.Resources>
And then you have to use that that converter in TextBlock's foreground property which is bound by Status. It will return appropriate color according to Status.
You can use <Run /> to combine text with binding text.
<TextBlock Name="txtStatus" Margin="10,2,0,0" Width="350" TextTrimming="WordEllipsis">
<Run Text="Status :" /> <Run Text="{Binding Status}" Foreground="{Binding Status, Converter={StaticResource StatusToColor}}"/>
</TextBlock>
You could also add a new property (StateColor) to your DataContext, wrap your textblock in a Border control and bind the background of this border to the property (simple but maybe against MVVM, because you get some UI stuff into your viewmodel).
An other way to achieve your goal is by using a StyleSelector, see:
http://zamjad.wordpress.com/2010/01/01/applying-style-conditionally/
http://blogs.u2u.be/diederik/post/2012/05/22/Using-Dynamic-XAML-in-Windows-8-Metro.aspx
cheers, Alex
Related
I have the following situation. I want to show a list of strings. In order to achieve that, I have a ListView bound to a collection of strings. In this collection, there are some empty strings. What I want is to show the following text when a empty string is present: "-empty-". This is what I got so far (the source code is for demonstration purpose only):
EmptyStringConverter.cs
public class EmptyStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string && string.IsNullOrWhiteSpace((string)value))
{
return "-empty-";
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
MainPage.xaml
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:EmptyStringConverter x:Key="EmptyStringConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EmptyStringConverter}}" Margin="0,0,0,5" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
var source = new[] { "", "String 1", "String 2" };
ListView.ItemsSource = source;
}
}
When a put a breakpoint in the Convert method in EmptyStringConverter class, the method is called in every single item except in the empty string. How can I achieve what I want?
Well the issue was in the place I'd check the last. It's your Legacy Binding that's causing the issue. I tried a code snippet myself and then I replaced it with yours piece by piece. The below line uses legacy binding
Text="{Binding Converter={StaticResource EmptyStringConverter}}"
Since you are using UWP you can switch to a Compile time binding that'll fix your problem your modified ListView XAML would be:
<ListView x:Name="ListView" >
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{x:Bind Converter={StaticResource EmptyStringConverter},Mode=OneTime}" Margin="0,0,0,5" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Please pay attention at two things:
the Text section of the Textbox in the DataTemplate, it uses x:Bind instead of Binding. Please do note that compile time binding is a oneTime binding by default (unless you explicitly mention the Mode) where as the Legacy Binding is a OneWay binding by default. For More information on compile time binding follow: xBind markup extension.
If you notice the DataTemplate declaration holds a DataType property, that helps the compile time binder to know what kind of data is it expecting. For more information on DataType follow: xBind markup extension.
All that being said, I would highly recommend using the Data Binding approach instead of the ListView.ItemSource=source as with the new Compile Time binding, alot of conversions are handled by the binding engine itself leading to less code and effort on your end. I've put up a sample on github for the same you can check it out: EmptyStringDemo
I'm writing a UserControl, which I intend to use on several pages. It should encapsulate the behaviour to be the same for all pages. But the content and the layout should be different.
On the UserControl I have a ListView whoes ItemSource is bound to a CollectionViewSource with grouping enabled.
<ListView
ItemSource="{Binding Source={StaticResource Collection}}"
ItemTemplate="{Binding GroupedDataTemplate}">
<ListView.GroupStyle>
<GroupSytele HeaderTemplate="{Binding HeaderDataTemplate}"/>
</ListView.GroupStyle>
</ListView>
The UserControl has the DependencyProperties "GroupedDataTemplate", "HeaderDataTemplate" for the layout and one "GroupedCollection" for the data.
On the page, where the UserControl is used, I defined the DataTemplates like:
<controls:MyUserControl
GroupedCollection="{Binding DataContext.MyDataCollection, ElementName=thePage}">
<controls:MyUserControl.GroupedDataTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
</controls:MyUserControl.GroupedDataTemplate>
<controls:MyUserControl.HeaderDataTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</controls:MyUserControl.HeaderDataTemplate>
</controls:MyUserControl>
My problem is, that the DataTemplate definition for "GroupedDataTemplate" works as expected, the description is shown. But for the "HeaderDataTemplate" it doesn't, it is shown only the ToString()-representation of the object.
The setter of the "HeaderDataTemplate" is called and the DataTemplate is assigned to the DependencyProperty of the UserControl.
If I replace the UserControl with the ListView itself, it works as expceted. Thus the binding works propperly to the Description and the Key, but it will only work for description if it is inside the UserControl.
For test purposes I have added a converter to the binding of the Key in the page and it is never called. I all cases, where I define a DataTemplate for an ItemTemplate (ListView or GridView) it works, but is doesn't for the HeaderTemplate of the GroupStyle.
What is my fault?
Very good question, it seems when you use Binding for the HeaderTemplate of GroupSytele, in the .g.cs file of your UserControl, it doesn't generate the update code for HeaderDataTemplate, it means when you define this HeaderDataTemplate property for example like this:
public static readonly DependencyProperty HeaderDataTemplateProperty = DependencyProperty.Register("HeaderDataTemplate", typeof(DataTemplate), typeof(UserGroupedListView), new PropertyMetadata(null));
public DataTemplate HeaderDataTemplate
{
get { return (DataTemplate)GetValue(HeaderDataTemplateProperty); }
set { SetValue(HeaderDataTemplateProperty, value); }
}
The get never gets called.
A workaround here is that you can change the Binding to x:Bind like this in your UserControl:
<ListView ItemsSource="{Binding Source={StaticResource Collection}}" ItemTemplate="{Binding GroupedDataTemplate}">
<ListView.GroupStyle>
<GroupStyle HeaderTemplate="{x:Bind HeaderDataTemplate}" />
</ListView.GroupStyle>
</ListView>
Basically you've done nothing wrong, but it seems data binding for the HeaderTemplate of GroupStyle can only work when it uses x:Bind.
I'm quite new to XAML trying to make grid with Toggle buttons
Something like this:
<GridView ItemSource="{Binding ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Content="{Binding Label}" IsEnabled="{Binding BEnabled}" IsChecked="{Binding BChecked, Mode=TwoWay}"/>
<DataTemplate>
</GridView.ItemTemplate>
</GridView>
I have
ObservableCollection<ButtonClass> ButtonCollection
also class for buttons
class ButtonClass
{
public string Label {get; set;}
public bool BEnabled {get; set:}
public bool BChecked {get;set;}
}
binding works when page loads buttons are displayed from ObservableCollection
But I want collection to update when button IsChecked value changes
also is there any way to bind function to click method like:
Click="{Binding DoWhenClicked}"
Now it just results in error I think that is because DoWhenClicked isn't in ItemSource.
Summary:
I want to have Grid of toggle buttons that binds to some sort of list/array/collection of data with label, checked status, enabled status.
When toggle button is checked I want it to reflect in my collection.
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
What is good way to do this.
I noticed that you asked several question about Template 10 before, so I supposed that you also used Template 10 here for MVVM pattern.
binding works when page loads buttons are displayed from ObservableCollection But I want collection to update when button IsChecked value changes
In Template 10 project, if you want to get notified when parameter in the data model (here means your ButtonClass), you can derive your class from BindableBase. If you check the BindableBase class you will find that it has done the work of INotifyPropertyChanged, it will be much easier here deriving from BindableBase directly rather than implementing INotifyPropertyChanged by yourself.
also is there any way to bind function to click method
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
Instead of Click event, I personally recommend you to using Command in MVVM pattern, and you may want to know which Button is clicked, so you can use CommandParameter here. I created a blank template 10 project and here is my sample:
<GridView x:Name="gridView" RelativePanel.Below="pageHeader" Margin="16,12,0,0" ItemsSource="{x:Bind ViewModel.ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Width="100" Content="{Binding Label}" IsEnabled="{Binding BEnabled}"
IsChecked="{Binding BChecked, Mode=TwoWay}" Command="{Binding ToggleBtnClickCommand}"
CommandParameter="{Binding Label}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The ButtonClass is like this:
public class ButtonClass : BindableBase
{
public string Label { get; set; }
public bool BEnabled { get; set; }
private bool _bChecked;
public bool BChecked
{
get { return _bChecked; }
set
{
_bChecked = value;
RaisePropertyChanged();
}
}
public ICommand ToggleBtnClickCommand { get; set; }
}
And the MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
public ObservableCollection<ButtonClass> ButtonCollection;
public MainPageViewModel()
{
ButtonCollection = new ObservableCollection<ButtonClass>();
for (int i = 0; i < 20; i++)
{
ButtonCollection.Add(new ButtonClass
{
Label = "TButton " + i,
BChecked = true,
BEnabled = true,
ToggleBtnClickCommand = new DelegateCommand<string>(ToggleBtnClicked)
});
}
}
public void ToggleBtnClicked(string obj)
{
var buttonLable = obj;
}
}
In case you need to check my sample, you can find my demo here.
There are a few concepts that are missing from your code that you need to properly implement for this to work
INotifyPropertyChanged -> Your ButtonClass class acts as a view model for your view. As such, for values to properly update UI and the other way around you need your class to implement it and all your properties to raise the PropertyChanged event when their values change (in the setter)
2-Way bindings -> If you want your ButtonClass instance to update when you click the button and you want your button to change state if you change the property in the ButtonClass, your bindings need to be Mode=TwoWay. You might want to explore the new Bindings {x:Bind} for better performance
Commands -> To bind the click event, you need to use the Command property. You would need to create a ICommand implementation, and create a property in your ButtonClass. The use Command property of the Button to bind to that Command.
Essentially, everything I mentioned are components of an MVVM framework, of which there are many out there. Here are some of the more popular ones: Caliburn, MVVMCross, Prism.
There's also a great starter kit for Windows 10 that will definitely kick start your app and contains a lot of this classes already built - Template10
How can I perform drag and drop operations without adding code to the code-behind file?
Can I do this with Attached Properties?
If so, then how?
At first thought, I would think that I could create an attached property and bind it to whatever drag item's property that's associated to dragging. When the state of that property changes, my attached property's valueChanged method handler would then execute the drag logic that is currently located in my code-behind file.
However, I have not identified such a property for drag status.
NOTE:
I am not using Prism because I am implementing a Windows Phone 8.1 app.
Thus, at this time Prism is not supported.
The following answer relates to the overall pattern of managing events of a UIElement with no code-behind.
Summary:
We can access a control that is using an attached property and within that attached property's metadata object, we can call an event handler when the value of the attached property changes.
The value of the attached property is set to "true" in our XAML file. This will immediately trigger the attached property's metadata object to update state and invoke a method handler for the change in value.
This example demonstrates two controls that will change color during their mouse enter event:
XAML code:
<Window x:Class="Temp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="clr-namespace:Temp.Behaviors"
Title="MainWindow" Height="350" Width="525">
<Canvas>
<Ellipse Width="50" Height="50" Fill="Gray" Canvas.Top="0"
behaviors:AttachedProperties.ActivateOnMouseEnter="True" />
<Ellipse Width="50" Height="50" Fill="Gray" Canvas.Left="200"
behaviors:AttachedProperties.ActivateOnMouseEnter="True" />
</Canvas>
</Window>
Attached property code:
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Temp.Behaviors
{
public class AttachedProperties : DependencyObject
{
public static readonly DependencyProperty ActivateOnMouseEnterProperty =
DependencyProperty.RegisterAttached("ActivateOnMouseEnter", typeof(bool), typeof(AttachedProperties), new PropertyMetadata(false, ActivateOnMouseEnter_ValueChanged));
private static void ActivateOnMouseEnter_ValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
Ellipse element = dependencyObject as Ellipse;
element.MouseEnter += (se, ev) =>
{
element.Fill = new SolidColorBrush(Colors.Orange);
};
}
public static void SetActivateOnMouseEnter(UIElement element, bool value)
{
element.SetValue(ActivateOnMouseEnterProperty, value);
}
}
}
NOTE:
In this code we are repeatedly subscribing to the mouse enter event without ever unsubscribing (which is bad). However, for simplicity, I leave the logic to manage subscriptions out.
kinda new to XAML, but I was wondering if it's possible to declare a string variable which contains the value of a Textbox.Text.
<System:String x:Key="AlarmMessage01">
<!-- Textbox text goes here --->
</System:String>
I'm not looking for a solution which depends on code-behind, purely XAML code, and I don't want to enter a static value either.
Can this even be done, and if so could you show me an example?
Kind regards Cvr
<Page.Resources>
<x:String x:Key="myStaticString" >Hello World</x:String>
</Page.Resources>
<TextBlock Text="{StaticResource myStaticString}" />
I have tested it in WinRT or Windows Store projects
If what you need is a value that can be updated dynamically, you can use Dynamic Resources or Data Binding
With Data Binding(this is probably the best approach):
In your ViewModel class:
public string TextBoxValue { get; set; }
public ViewModel(string text)
{
TextBoxValue = text;
}
In your Code-Behind:
public CurrentPage()
{
this.BindingContext = new ViewModel("Text to be displayed");
}
In your XAML file:
<TextBlock Text="{Binding TextBoxValue}" />
And that's that about it.
Meanwhile, if you want to go with Dynamic Resources:
In code-behind file(wherever you want to update the value), you have:
this.Resources["myStringValue"] = "Text to be displayed";
And in XAML, you have:
<TextBlock Text="{DynamicResource myStringValue}" />