I'm currently learning how to develop and building an app for windows phone 7.
If a certain value is true, I need to add a TextBlock to the ListBox before a TextBlock (say its name is x:Name="dayTxtBx").
I am currently using
dayListBox.Items.Add(dayTxtBx);
to add the text box.
Any help very much appreciated!
Thanks
This is pretty easy to do if you're using a DataTemplate and a ValueConverter and passing the whole object into the ListBox (rather than just a string). Assuming you have some object that looks like:
public class SomeObject: INotifyPropertyChanged
{
private bool mTestValue;
public bool TestValue
{
get {return mTestValue;}
set {mTestValue = value; NotifyPropertyChanged("TestValue");}
}
private string mSomeText;
public string SomeText
{
get {return mSomeText;}
set {mSomeText = value; NotifyPropertyChanged("SomeText");}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if ((name != null) && (PropertyChanged != null))
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
You can make a converter that looks like:
public class BooleanVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && (bool)value)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And add the converter to your XAML like so:
<UserControl x:Class="MyProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<UserControl.Resources>
<local:BooleanVisibilityConverter x:Key="BoolVisibilityConverter" />
<UserControl.Resources>
Then you could have the ListBox defined in XAML like so:
<Listbox>
<Listbox.ItemTemplate>
<DataTemplate>
<StackPanel Orentation="Horizontal" >
<TextBlock Text="Only Show If Value is True" Visibility={Binding TestValue, Converter={StaticResource BoolVisibilityConverter}} />
<TextBlock Text="{Binding SomeText}" />
</StackPanel>
</DataTemplate>
</Listbox.ItemTemplate>
</Listbox>
Might seem like a lot, but it's really pretty simple once you get started. A great way to learn more about data binding and converters is at Jesse Liberty's blog ( http://jesseliberty.com/?s=Windows+Phone+From+Scratch ).
Related
I am developing WPF application in which I am using TextBox to write some text. I want to change foreground color of my textbox to red whenever user encloses particular text in textbox with <qs> (at start) and <qe> at end. Similarly text color should change to green if user encloses some text with <as> at start and <ae> at end. I searched a lot online but didn't find any information. Any help is highly appreciated.
Output is expected as depicted in image
Also, I am using MVVM in my application.
As #Erno de Weerd commented you can use a custom ValueConvertert for this. Another way I can think of is to inherit TextBox and override TextProperty metadata to react to changes on that property. The following sample contains the two approaches:
The XAML:
<Window x:Class="WpfApp.MainWindow"
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"
xmlns:wpfApp="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<wpfApp:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<wpfApp:TextToColorConverter x:Key="TextToColorConverter"/>
</Window.Resources>
<StackPanel >
<TextBox Text="{Binding SomeString, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding SomeString, Mode=OneWay, Converter={StaticResource TextToColorConverter}}"
Margin="0,0,0,20"/>
<wpfApp:MyCustomTextBox Text="{Binding SomeString, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Window>
MainWindowViewModel only contains the bound property and implements INotifyPropertyChanged:
public class MainWindowViewModel : INotifyPropertyChanged
{
private string _someString;
public string SomeString
{
get => _someString;
set
{
if (value == _someString) return;
_someString = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
First approach, IValueConverter:
public class TextToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var str = (string)value;
if (str == null) return new SolidColorBrush(Colors.Black);
return str.StartsWith("<qs>") && str.EndsWith("<qe>") ? new SolidColorBrush(Colors.Red) :
str.StartsWith("<as>") && str.EndsWith("<ae>") ? new SolidColorBrush(Colors.Green) :
new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And the second one, we inherit from TextBox:
public class MyCustomTextBox : TextBox
{
static MyCustomTextBox()
{
TextProperty.OverrideMetadata(typeof(MyCustomTextBox), new FrameworkPropertyMetadata(OnTextChanged));
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (MyCustomTextBox)d;
var str = (string)e.NewValue;
SolidColorBrush foreGround;
if (str == null)
{
foreGround = new SolidColorBrush(Colors.Black);
}
else
{
foreGround = str.StartsWith("<qs>") && str.EndsWith("<qe>") ? new SolidColorBrush(Colors.Red) :
str.StartsWith("<as>") && str.EndsWith("<ae>") ? new SolidColorBrush(Colors.Green) :
new SolidColorBrush(Colors.Black);
}
control.Foreground = foreGround;
}
}
I personally prefer the second approach, creating my own custom control allows me to use that control in as many views as I want without needing to include always an instance of a converter in my XAML, furthermore, if you were going to need some more custom behavior then you just add that behavior to your custom control instead of writing many many other converters.
I have a project in which I need to bind the background of a TextBox to a value in an array where the index is a property in the DataContext:
Binding backgroundBinding= new Binding();
backgroundBinding.Path = new PropertyPath($"Elements[{Index}].Value");
I've been creating the binding in the codebehind, but want to find a better and more elegant way to do it. Would I have to create a custom converter, or is there some way to reference the Index property in the XAML?
So you've got two options here. I think you're asking for the first one. I've set up two properties in my viewmodel - one for the array of colours, and then one for the index I want to use. I'm binding to them through a MultiConverter to return the correct colour from the array. This will allow you to update your selected index at runtime, and have the background change to the newly selected colour. If you just want a static index that never changes, you should use implement IValueConverter instead of a IMultiValueConverter, and then pass the index in using the ConverterParameter property.
As a side note, I've chosen to implement the array as type Color. SolidColorBrush objects are expensive and doing it this way will help reduce that cost.
public class ViewModel : INotifyPropertyChanged
{
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private Color[] _backgroundColours = new Color[] { Colors.AliceBlue, Colors.Aqua, Colors.Azure };
public Color[] BackgroundColours
{
get => _backgroundColours;
set
{
_backgroundColours = value;
OnPropertyChanged();
}
}
private int _backgroundIndex = 1;
public int ChosenIndex
{
get => _backgroundIndex;
set
{
_backgroundIndex = value;
OnPropertyChanged();
}
}
}
...
public class BackgroundConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var backgroundColours = values[0] as Color[];
var chosenIndex = (int)values[1];
return new SolidColorBrush(backgroundColours[chosenIndex]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
...
<Grid>
<Grid.DataContext>
<local:ViewModel />
</Grid.DataContext>
<Grid.Resources>
<local:BackgroundConverter x:Key="backgroundConverter"/>
</Grid.Resources>
<TextBox>
<TextBox.Background>
<MultiBinding Converter="{StaticResource backgroundConverter}">
<Binding Path="BackgroundColours" />
<Binding Path="ChosenIndex" />
</MultiBinding>
</TextBox.Background>
</TextBox>
</Grid>
I've bound two radio buttons in my Child window to an Enum in my ViewModel which is constructed in the Main window. The binding works as expected but I have noticed a very odd behavior which I can't solve. I have provided all the code here so you can reconstruct the problem easily for yourself.
Here are the steps to see this odd behavior:
Click on the button in the MainWindow
The ChildWindow opens and the RadioButton is set to User
Choose Automatic and then Choose User again
Close the ChildWindow and reopen it again! Try to change the RadioButton to Automatic. It won't change!
<Window x:Class="RadioButtonBinding.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">
<Button Content="Display Child Window" Click="DisplayChildWindow"/>
</Window>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var viewModel = new ViewModel();
DataContext = viewModel;
}
private void DisplayChildWindow(object sender, RoutedEventArgs e)
{
var win = new ChildWindow {DataContext = (ViewModel) DataContext};
win.ShowDialog();
}
}
<Window x:Class="RadioButtonBinding.ChildWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:radioButtonBinding="clr-namespace:RadioButtonBinding"
Title="ChildWindow" Height="300" Width="300">
<Window.Resources>
<radioButtonBinding:EnumBooleanConverter x:Key="EnumBooleanConverter"/>
</Window.Resources>
<StackPanel>
<RadioButton Content="Automatic"
GroupName="CalcMode"
IsChecked="{Binding Path=CalcMode,
Converter={StaticResource EnumBooleanConverter},
ConverterParameter={x:Static radioButtonBinding:CalcMode.Automatic}}"/>
<RadioButton Content="Custom"
GroupName="CalcMode"
IsChecked="{Binding Path=CalcMode,
Converter={StaticResource EnumBooleanConverter},
ConverterParameter={x:Static radioButtonBinding:CalcMode.User}}"/>
</StackPanel>
</Window>
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private CalcMode calcMode = CalcMode.User;
public CalcMode CalcMode
{
get { return calcMode; }
set
{
calcMode = value;
RaisePropertyChanged("CalcMode");
}
}
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var paramEnum = parameter as Enum;
var valueEnum = value as Enum;
return Equals(paramEnum, valueEnum);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var parameterEnum = parameter as Enum;
if (parameterEnum == null)
return DependencyProperty.UnsetValue;
return parameterEnum;
}
}
public enum CalcMode : byte
{
Automatic,
User,
}
UPDATE:
I suspect it must be the Converter but I don't know why? It just falls into a loop.
EDIT
What about converting the enum to bool as follows?
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null || !(bool)value)
return DependencyProperty.UnsetValue;
var parameterEnum = parameter as Enum;
return parameterEnum;
}
Is it correct that it is not currently possible to bind to any Nullable<T> in Universal XAML Apps?
I found this link from 2013:
https://social.msdn.microsoft.com/Forums/en-US/befb9603-b8d6-468d-ad36-ef82a9e29749/textbox-text-binding-on-nullable-types?forum=winappswithcsharp
Stating that:
Binding to nullable values isn't supported in Windows 8 Store apps. It just didn't make it into this release. We've already got bugs on this behavior for v.Next.
But can it really be that this has not been fixed yet?
My Binding:
<TextBox Text="{Binding Serves, Mode=TwoWay}" Header="Serves"/>
My Property:
public int? Serves
{
get { return _serves; ; }
set
{
_serves = value;
OnPropertyChanged();
}
}
And the error I get in my output:
Error: Cannot save value from target back to source.
BindingExpression:
Path='Serves'
DataItem='MyAssembly.MyNamespace.RecipeViewModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'; target element is 'Windows.UI.Xaml.Controls.TextBox' (Name='null'); target property is 'Text' (type 'String').
Seems like it's not fixed. As XAML is using a build-in converter, in this case you can probably exchange it with your own, dealing with nullables:
XAML:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel.Resources>
<local:NullConverter x:Key="NullableIntConverter"/>
</StackPanel.Resources>
<TextBox Text="{Binding Serves, Mode=TwoWay, Converter={StaticResource NullableIntConverter}}" Header="Serves"/>
</StackPanel>
Code behind:
public class NullConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{ return value; }
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
int temp;
if (string.IsNullOrEmpty((string)value) || !int.TryParse((string)value, out temp)) return null;
else return temp;
}
}
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private int? _serves;
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
public int? Serves
{
get { return _serves; }
set { _serves = value; RaiseProperty("Serves"); }
}
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
}
I have a UserControl in which I have a DataDrid and in that DataGrid I have two ComboBoxes. Now what I want to do is when I select any item from both the ComboBoxes the Button which is outside the DataGrid should get enabled.
My DataGrid is bind to an ItemSource so does the Comboboxes.
I tries to use MuliDatatriggers but they failed as button is outside the DataGrid so those ComboBoxes will not be available to it.
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="Combo1" ItemsSource="{Binding Lst1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Code1" SelectedValue="{Binding CodeID1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ComboBox Name="Combo2" ItemsSource="{Binding Lst2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Code2" SelectedValue="{Binding CodeID2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Name="Add" IsEnabled="{Binding IsAddEnabled,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
There is a lot of answers to this question already posted.
For example: Enable text box when combobox item is selected
The better way for you is to apply MVVM to your application.
I agree with #MikroDel with the MVVM that the only way to work right in wpf.. I do somthing like this but not with two cmbs and not on datagrid but that not need to be different at all because in each combo you set the selected index to your property on the viewModel and the same for button.
in this exemple i use RelayCommand you can read hare about using it, but that not this q subject.
In addition I use a convertor 'cause like the button be enabled also if selected index = 0 so it implementd very simply
namespace MCSearchMVVM
{
class MCBindButtonToComboBox : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return false;
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ throw new NotImplementedException(); }
}
}
Now to the real stuff ;)
Little advice before that is I like allways put the view(.xaml file) and the vm(.cs file) on the same folder, that why i find this example very fast lol
First we begon with the view:
<UserControl x:Class="MCSearchMVVM.AddFilePage"
...
xmlns:local="clr-namespace:MCSearchMVVM"
...>
<UserControl.Resources>
<local:MCBindButtonToComboBox x:Key="enableCon"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid.Background>
...
</Grid.Background>
<Button Content="Browse.."
...
Command="{Binding BrowseCommand}"
IsEnabled="{Binding FileKindIndexSelected,
Converter={StaticResource enableCon}}"
.../>
<ComboBox ... SelectedIndex="{Binding FileKindIndexSelected, Mode=TwoWay}" ... >
...
</ComboBox>
...
</Grid>
Now the ViewModel :
public class AddFileViewModel : ObservableObject, IPageViewModel
{
...
private int _fileKindIndexSelected;
public int FileKindIndexSelected
{
get { return _fileKindIndexSelected; }
set { SetField(ref _fileKindIndexSelected, value, "FileKindIndexSelected");}
}
...
}
And the SetField func
public abstract class ObservableObject : INotifyPropertyChanged
{
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#region INotifyPropertyChanged
public virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion // INotifyPropertyChanged
}
}
I Hope that direction was helpfull..
And sorry for my bad English =))