Winui3 ListView - show button between list items - xaml

I'm trying to learn WinUI by building an app that can help with planning holidays. One of the things I'm trying to do is build a UI that shows a list of destinations (currently using a ListView), and then have a button to the right essentially pointing to the line between them that could pop up a UI for 'travel' between the two destinations.
This is a mockup of what I'm trying to achieve:
The buttons with the badly drawn plane and train icons should represent the travel between the two locations and therefore should be in the middle.
I've mainly been flailing about trying to get it to work using a grid inside of the DataTemplate, but haven't been able to get the 'half-way' alignment without making the gap between the destinations bigger.
Does anyone have any suggestions as to how I could achieve this, or is it a fools errand?

I guess you need to create a user control (or a custom control). Something like this:
CustomList.xaml
<UserControl
x:Class="UserControls.CustomList"
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:local="using:UserControls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="RootContainer" />
</UserControl>
CustomList.xaml
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using System.Collections.Generic;
using Windows.Foundation.Collections;
namespace UserControls;
public sealed partial class CustomList : UserControl
{
public static readonly DependencyProperty MainItemsProperty = DependencyProperty.Register(
nameof(MainItems),
typeof(IEnumerable<string>),
typeof(CustomList),
new PropertyMetadata(default, (d, e) => (d as CustomList)?.OnItemsPropertyChanged()));
public static readonly DependencyProperty SubItemsProperty = DependencyProperty.Register(
nameof(SubItems),
typeof(IEnumerable<string>),
typeof(CustomList),
new PropertyMetadata(default, (d, e) => (d as CustomList)?.OnItemsPropertyChanged()));
public CustomList()
{
this.InitializeComponent();
this.RootContainer.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });
this.RootContainer.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });
}
public IEnumerable<string> MainItems
{
get => (IEnumerable<string>)GetValue(MainItemsProperty);
set => SetValue(MainItemsProperty, value);
}
public IEnumerable<string> SubItems
{
get => (IEnumerable<string>)GetValue(SubItemsProperty);
set => SetValue(SubItemsProperty, value);
}
private ICollectionView? MainItemsCollectionView { get; set; }
private ICollectionView? SubItemsCollectionView { get; set; }
private void OnItemsPropertyChanged()
{
CollectionViewSource mainCollectionViewSource = new()
{
Source = MainItems
};
if (MainItemsCollectionView is not null)
{
MainItemsCollectionView.VectorChanged -= ItemsCollectionView_VectorChanged;
}
MainItemsCollectionView = mainCollectionViewSource.View;
MainItemsCollectionView.VectorChanged += ItemsCollectionView_VectorChanged;
CollectionViewSource subCollectionViewSource = new()
{
Source = SubItems
};
SubItemsCollectionView = subCollectionViewSource.View;
RefreshList();
}
private void ItemsCollectionView_VectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs #event)
{
RefreshList();
}
private void RefreshList()
{
this.RootContainer.Children.Clear();
this.RootContainer.RowDefinitions.Clear();
if (MainItemsCollectionView is not null)
{
UpdateRowDefinitions(MainItemsCollectionView.Count);
for (int i = 0; i < MainItemsCollectionView.Count; i++)
{
TextBlock mainItemTextBlock = new()
{
Text = MainItemsCollectionView[i] as string,
};
Grid.SetColumn(mainItemTextBlock, 0);
Grid.SetRow(mainItemTextBlock, i * 2);
Grid.SetRowSpan(mainItemTextBlock, 2);
this.RootContainer.Children.Add(mainItemTextBlock);
if (SubItemsCollectionView?.Count > i)
{
TextBlock subItemTextBlock = new()
{
Text = SubItemsCollectionView[i] as string,
};
Grid.SetColumn(subItemTextBlock, 1);
Grid.SetRow(subItemTextBlock, (i * 2) + 1);
Grid.SetRowSpan(subItemTextBlock, 2);
this.RootContainer.Children.Add(subItemTextBlock);
}
}
}
}
private void UpdateRowDefinitions(int mainItemsCount)
{
int requiredRowsCount = mainItemsCount * 2;
while (this.RootContainer.RowDefinitions.Count != requiredRowsCount)
{
if (this.RootContainer.RowDefinitions.Count < requiredRowsCount)
{
this.RootContainer.RowDefinitions.Add(new RowDefinition()
{
Height = new GridLength(0, GridUnitType.Auto),
});
continue;
}
if (this.RootContainer.RowDefinitions.Count > requiredRowsCount)
{
this.RootContainer.RowDefinitions.RemoveAt(this.RootContainer.RowDefinitions.Count - 1);
continue;
}
}
}
}
MainPageViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
namespace UserControls;
public partial class MainPageViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<string> mainItems = new()
{
"New York",
"London",
"Edinburgh"
};
[ObservableProperty]
private ObservableCollection<string> subItems = new()
{
"Airplane",
"Train",
};
}
MainPage.xaml
<Grid>
<local:CustomList
MainItems="{x:Bind ViewModel.MainItems, Mode=OneWay}"
SubItems="{x:Bind ViewModel.SubItems, Mode=OneWay}" />
</Grid>

Related

Xamarin Forms Using Reflexion to get Property of Bindable Property

What i want is, when the value is changed, it should call CreateChart()and use the new values.
I try to call in an onPropertyChange method OnValueChanged a bindable Property with reflection, but the property is always null and i dont get the value of the property Value
public partial class CorrectWrongRingChart : ContentView
{
public CorrectWrongRingChart()
{
InitializeComponent();
}
public static readonly BindableProperty ChartProperty =
BindableProperty.Create(
nameof(CorrectWrongChart),
typeof(Chart),
typeof(CorrectWrongRingChart));
public Chart CorrectWrongChart
{
get { return (Chart)GetValue(ChartProperty); }
set => SetValue(ChartProperty, value);
}
public static readonly BindableProperty ValueProperty =
BindableProperty.Create(
nameof(Value),
typeof(CorrectWrongValue),
typeof(CorrectWrongRingChart),
propertyChanged: OnValueChanged);/*(b, o, n) => { ((CorrectWrongRingChart)b).OnPropertyChanged("Text");});*/
public CorrectWrongValue Value
{
get { return (CorrectWrongValue)GetValue(ValueProperty); }
set => SetValue(ValueProperty, value);
}
private static void OnValueChanged(BindableObject bindable, object oldValue, object newValue)
{
((CorrectWrongRingChart)bindable).OnPropertyChanged("Text");
//((CorrectWrongRingChart)bindable).OnPropertyChanged("Correct");
//((CorrectWrongRingChart)bindable).OnPropertyChanged("Wrong");
var valueProperty = ValueProperty.GetType().GetProperty("Value");
var value = (CorrectWrongValue)valueProperty.GetValue("Value");
var ChartProperty = ValueProperty.GetType().GetProperty("CorrectWrongChart");
if (value != null)
{
ChartProperty.SetValue("CorrectWrongChart", CreateChart(value));
}
}
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(CorrectWrongRingChart),
defaultValue: string.Empty);
public string Text => $"{Value?.CorrectCount ?? 0}/{Value?.TotalCount ?? 0}";
//public double Correct => Value.CorrectPercentage;
//public double Wrong => Value.WrongPercentage;
private static Chart CreateChart(CorrectWrongValue value)
{
var chart = new Microcharts.DonutChart();
chart.IsAnimated = false;
ChartEntry corretEntry = new ChartEntry((float)value.CorrectPercentage)
{
Color = SKColor.Parse("#00FF00")
};
ChartEntry wrongEntry = new ChartEntry((float)value.WrongPercentage)
{
Color = SKColor.Parse("#FF0000")
};
chart.Entries = new List<ChartEntry>() { corretEntry, wrongEntry };
return chart;
}
}
Xaml:
<Grid >
<forms:ChartView x:Name="chart1" WidthRequest="130" HeightRequest="130" Chart="{Binding CorrectWrongChart, Source={x:Reference Root}}">
</forms:ChartView>
<Label Text="{ Binding Text, Source={x:Reference Root} }"
VerticalOptions="Center"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center"
TextColor="Black"
FontSize="19"
FontFamily="{ StaticResource AppBoldFontFamily }" />
</Grid>
If you want to get Value when the value is changed , you could get it directly like following
private static void OnValueChanged(BindableObject bindable, object oldValue, object newValue)
{
var currentValue = newValue;
// do something you want
// you could get other property like following
// var view = bindable as CorrectWrongRingChart;
// var currentText = view.Text;
}
The property Value will change automatically when we change the value of it source .So we don't need to invoke the following lines any more , which maybe will lead to infinite loop .
if (value != null)
{
ChartProperty.SetValue("CorrectWrongChart", CreateChart(value));
}

display picker when clicking in imagebutton xamarin

i want to display a picker (or list) when i click in my imageButton but it didn't work, but when i create only a picker , it worked.
Xaml
<StackLayout Orientation="Horizontal" x:Name="stacklayout">
<Entry Placeholder="préciser l'entité correspondante"
ClearButtonVisibility="WhileEditing" x:Name="entit"/>
<ImageButton Source="list.png" WidthRequest="30" HeightRequest="30" x:Name="listEntité" Clicked="listEntité_Clicked"/>
Xaml.cs
private void listEntité_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://192.168.1.3:3000/api/adepApi/GetCurrencyLists");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.GetAsync("http://192.168.1.3:3000/api/adepApi/GetCurrencyLists");
var content = await response.Content.ReadAsStringAsync();
ResponseDataD EL = JsonConvert.DeserializeObject<ResponseDataD>(content);
p.ItemsSource= EL.Data.DeviseList;
stacklayout.Children.Add(p.ItemsSource);
}
class DeviseModel
public class DeviseModel
{
public class DeviseL
{
// public string devis;
[JsonProperty("Label")]
public string Devis { get; set; }
[JsonProperty("Value")]
public int id { get; set; }
}
public class ResponseDataD
{
public RootModelDevise Data;
}
public class RootModelDevise : INotifyPropertyChanged
{
List<DeviseL> deviseList;
[JsonProperty("list")]
public List<DeviseL> DeviseList
{
get { return deviseList; }
set
{
if (deviseList != value)
{
deviseList = value;
OnPropertyChanged();
}
}
}
DeviseL itemDevise;
public DeviseL ItemDevise
{
get { return itemDevise; }
set
{
if (itemDevise != value)
{
itemDevise = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}}
i have an error in the line
stacklayout.Children.Add(p.ItemsSource);
under p.itemsSource :unable to convert from 'System.Collections.Ilist' to 'Xamarin.Forms.View'
i have tried to add picker by imageButton click event , but nothing appears.
Firstly, please confirm that the EL.Data.DeviseList have list value, you have list binding to Picker, then adding picker to current contentPage.
public partial class Page16 : ContentPage
{
private List<string> list;
public Page16()
{
InitializeComponent();
list = new List<string>();
list.Add("test 1");
list.Add("test 2");
list.Add("test 3");
list.Add("test 4");
}
private void imagebutton1_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
p.ItemsSource = list;
stacklayout.Children.Add(p);
}
}
<StackLayout x:Name="stacklayout">
<ImageButton
x:Name="imagebutton1"
Clicked="imagebutton1_Clicked"
HeightRequest="30"
Source="plu3.png"
WidthRequest="30" />
</StackLayout>
Update:
private void imagebutton1_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
p.SelectedIndexChanged += P_SelectedIndexChanged;
p.ItemsSource = devs;
p.ItemDisplayBinding = new Binding("Devis");
stacklayout.Children.Add(p);
}
private void P_SelectedIndexChanged(object sender, EventArgs e)
{
Picker p = sender as Picker;
DeviseL selectitem = p.SelectedItem as DeviseL;
entit.Text =selectitem.Devis;
}

Change of color de my custom enter once is clicked my button

im working with custon entry rendered, i need to hear from xaml in my custom render when i clicked my button
i have this code in my xaml
<local:MyEntry eventRefresh="true">
when i clicked my button this function is actived
private async void Execute(object sender)
{
var entry = ((MyEntry)view);
entry.eventRefresh = "false";
but my EntryRendered donot hear the change
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var element = Element as MyEntry;
You should define the property eventRefresh as Bindable Property .
in your custom Entry
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace xxx
{
public class MyEntry:Entry,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static readonly BindableProperty eventRefreshProperty = BindableProperty.Create("eventRefresh", typeof(bool), typeof(MyEntry), true,propertyChanged:(obj,oldValue,newValue)=> {
//var entry = obj as MyEntry;
// entry.Text = newValue.ToString();
});
bool refresh;
public bool eventRefresh
{
get { return refresh; }
set {
if(refresh !=value)
{
refresh = value;
NotifyPropertyChanged("eventRefresh");
}
}
}
public MyEntry()
{
}
}
}
in xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<local:MyEntry eventRefresh="{Binding Refresh}" BackgroundColor="{Binding BGcolor}" WidthRequest="200" HeightRequest="50" />
<Button Command="{Binding ClickCommand}" />
</StackLayout>
in View Model
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Color color;
public Color BGcolor
{
get { return color; }
set
{
if (color != value)
{
color = value;
NotifyPropertyChanged("BGcolor");
}
}
}
bool refresh;
public bool Refresh
{
get { return refresh; }
set
{
if (refresh != value)
{
refresh = value;
NotifyPropertyChanged("Refresh");
}
}
}
public ICommand ClickCommand { get; set; }
public MyViewModel()
{
BGcolor = Color.LightPink;
ClickCommand = new Command(()=> {
BGcolor = Color.Red;
});
}
}
in Custom Renderer
using System.ComponentModel;
using Android.Content;
using xxx;
using xxx.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:ExportRenderer(typeof(MyEntry),typeof(NyEntryRenderer))]
namespace xxx.Droid
{
public class NyEntryRenderer : EntryRenderer
{
public NyEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Element.TextChanged += Element_TextChanged;
}
}
private void Element_TextChanged(object sender, TextChangedEventArgs e)
{
// var content = Element.Text;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyEntry.BackgroundColorProperty.PropertyName)
{
// will been invoked when click button
}
}
}
}
Make your view model like this.
public class YourViewModel
{
public Command command
{
get
{
return new Command(() => {
//Change here button background colors
BackgroundColor = Color.Green;
});
}
}
private _backgroundColor = Color.White;
public Color BackgroundColor
{
get { return _backgroundColor;}
set
{
if (value == _backgroundColor)
return;
_backgroundColor = value;
NotifyOnPropertyChanged(nameof(BackgroundColor));
}
}
}
Your XAML
<local:MyEntry Text="{Binding Password}" Placeholder="Enter" />
<Button Text="send" Command="{Binding command}" BackgroundColor="{Binding BackgroundColor}"></Button>

Why my WP emulator stops on splashscreen

I created a WP Class Library BusinessLogic project which is composed by these three class
1) Bottle Class
namespace BusinessLogic
{
public class Bottle : INotifyPropertyChanged
{
// Due to INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
// Proprietà Title
private string title;
public string Title
{
set
{
if (title != value)
{
title = value;
OnPropertyChanged("Title");
}
}
get
{
return title;
}
}
// Proprietà PhotoFileName
private string photoFileName;
public string PhotoFileName
{
set
{
if (photoFileName != value)
{
photoFileName = value;
OnPropertyChanged("PhotoFileName");
}
}
get
{
return photoFileName;
}
}
protected virtual void OnPropertyChanged(string propChanged)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propChanged));
}
}
}
2) Bottles Class
namespace BusinessLogic
{
public class Bottles : INotifyPropertyChanged
{
// Due to INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
// Proprietà MainTitle
private string mainTitle;
public string MainTitle
{
set
{
if (mainTitle != value)
{
mainTitle = value;
OnPropertyChanged("MainTitle");
}
}
get
{
return mainTitle;
}
}
// Proprietà bottles
private ObservableCollection<Bottle> bottleSet = new ObservableCollection<Bottle>();
public ObservableCollection<Bottle> BottleSet
{
set
{
if (bottleSet != value)
{
bottleSet = value;
OnPropertyChanged("BottleSet");
}
}
get { return bottleSet; }
}
protected virtual void OnPropertyChanged(string propChanged)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propChanged));
}
}
}
3) BottlesPresenter Class
namespace BusinessLogic
{
public class BottlesPresenter : INotifyPropertyChanged
{
// Due to INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
// Proprietà BottleMatrix
private Bottles bottlesMatrix;
public Bottles BottlesMatrix
{
protected set
{
if (bottlesMatrix != value)
{
bottlesMatrix = value;
OnPropertyChanged("BottlesMatrix");
}
}
get { return bottlesMatrix; }
}
public BottlesPresenter()
{
XmlSerializer xml = new XmlSerializer(typeof(Bottles));
using (StreamReader fileReader = new StreamReader(#"C:\Stuff\WindowsPhone\AppLearningHowTo2\AppLearningHowTo2\DAL\DB.xml"))
{
BottlesMatrix = (Bottles)xml.Deserialize(fileReader);
}
}
protected virtual void OnPropertyChanged(string propChanged)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propChanged));
}
}
}
The BottlePresenter constructor should deserialize from an xml file located into the file system. It contains the following tags
<?xml version="1.0"?>
<Bottles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MainTitle>MainTitle</MainTitle>
<Bottleset>
<Bottle>
<Title>Title1</Title>
<PhotoFileName>PhotoFileName1</PhotoFileName>
</Bottle>
<Bottle>
<Title>Title2</Title>
<PhotoFileName>PhotoFileName2</PhotoFileName>
</Bottle>
</Bottleset>
</Bottles>
Then I created a WP Application and I made a reference to the BusinessLogic.dll project library.
In the MainPage.xaml file I put the XML namespace declaration
xmlns:businesslogic="clr-namespace:BusinessLogic;assembly=BusinessLogic"
I then instantiated the BottlesPresenter class in the MainPage.xaml Resources collection
<phone:PhoneApplicationPage.Resources>
<businesslogic:BottlesPresenter x:Key="bottlesPresenter" />
</phone:PhoneApplicationPage.Resources>
And finally put a TextBlock in the content area with a binding to that resource:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Source={StaticResource bottlesPresenter},
Path=Bottles.MainTitle}" />
Unfortunately I launch the debugger, the emulator switch on, reach the splashscreen and doesn't go on.
In a nutshell: I can't reach to create an instance of the BottlesPresenter class.
I banged my head against the wall for weeks without finding a solution.
Please could someone give me a hand?
Thank you very much
Antonio
Emulator behaves like that when WP7 cannot construct Application object. From question, I see only 1 reference from Application to your code. It's BottlePresenter in Resources.
XamlLoader tries to create instance of this type.
See what's inside your BottlePresenter constructur:
public BottlesPresenter()
{
XmlSerializer xml = new XmlSerializer(typeof(Bottles));
using (StreamReader fileReader = new StreamReader(
#"C:\Stuff\WindowsPhone\AppLearningHowTo2\AppLearningHowTo2\DAL\DB.xml"))
{
BottlesMatrix = (Bottles)xml.Deserialize(fileReader);
}
}
First line is OK.
Second line is OK.
Third line causes exception, because path "C:\Stuff\WindowsPhone\AppLearningHowTo2\AppLearningHowTo2\DAL\DB.xml" is not acceptable on Windows Phone.
All files you can access is Content of your XAP, Resources in your assembly, and files in Isolated Storage.
Following articles might help you All about WP7 Isolated Storage - Read and Save Text files

Access named item inside ItemTemplate

I have the following scenario:
<Button Click="ClickHandler">Click Me</Button>
<TextBox x:Name="MyInputTextBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="MyRepeatTextBox" Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If MyRepeatTextBox.Text == MyInputTextBox.Text I want to change the color of MyRepeatTextBox.Background to Green. If MyRepeatTextBox.Text is blank I want to change the color to red. How would I implement the button click handler?
Not sure a button event would be the best for this.
As DataTriggers are again not brought outside the WPF world, those are out. There's no IMultiValueConverter either. Behaviours are currently out, but there is a nice codeplex project for them. I would use that
public class MatchTextForegroundBehaviour : Behavior<TextBox>
{
private TextBox _attachedElement;
public static readonly DependencyProperty MatchForegroundProperty =
DependencyProperty.Register("MatchForeground", typeof(Brush),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(new SolidColorBrush(Colors.Green), OnMatchForegroundChanged));
public static readonly DependencyProperty FallbackForegroundProperty =
DependencyProperty.Register("FallbackForeground", typeof(Brush),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(new SolidColorBrush(Colors.Black), OnFallbackForegroundChanged));
public static readonly DependencyProperty TextToMatchProperty =
DependencyProperty.Register("TextToMatch", typeof(string),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(null, OnTextToMatchChanged));
public Brush MatchForeground
{
get { return (Brush)GetValue(MatchForegroundProperty); }
set { SetValue(MatchForegroundProperty, value); }
}
public Brush FallbackForeground
{
get { return (Brush)GetValue(FallbackForegroundProperty); }
set { SetValue(FallbackForegroundProperty, value); }
}
public string TextToMatch
{
get { return (string)GetValue(TextToMatchProperty); }
set { SetValue(TextToMatchProperty, value); }
}
/// <summary>
/// Event when the behavior is attached to a element.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
_attachedElement = AssociatedObject;
if(_attachedElement != null)
_attachedElement.TextChanged += (s,e) => ChangeForeground();
}
private static void OnMatchForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private static void OnTextToMatchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private static void OnFallbackForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private void ChangeForeground()
{
if (_attachedElement == null) return;
if (string.IsNullOrEmpty(TextToMatch)) return; // change foreground to red?
if (_attachedElement.Text == TextToMatch)
{
_attachedElement.Foreground = MatchForeground;
}
else
{
_attachedElement.Foreground = FallbackForeground;
}
}
}
And the xaml
<TextBox x:Name="MyRepeatTextBox" Text="{Binding}"
TextToMatch="{Binding Text, ElementName=MyInputTextBox}"
FallbackForeground="Black" MatchForeground="Green" />
If a button click event is really how you want to do this, you can try the following. I have not compiled this against WinRT, but I think everything used is in WinRT.
Use the following ExtensionMethod
internal static class TreeExtensions
{
public static T GetChildElement<T>(this DependencyObject element) where T :FrameworkElement
{
if (element == null) return null;
if(element.GetType() == typeof(T)) return (T)element;
T childAsT = null;
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(element, i);
childAsT = child.GetChildElement<T>();
if (childAsT != null) break;
}
return childAsT;
}
}
Inside the button click event you would do the following (assuming you gave the ItemsControl a name of itemsControl:
foreach (var item in itemsControl.Items)
{
var element = itemsControl.ItemContainerGenerator.ContainerFromItem(item);
var textblock = element.GetChildElement<TextBlock>();
if (textblock != null)
{
if (textblock.Text == MyInputTextBox.Text)
textblock.Foreground = new SolidColorBrush(Colors.Green);
else
textblock.Foreground = new SolidColorBrush(Colors.Black);
}
}