I'm making a chating program.
I designed chat room list using XAML.
<GridViewColumn x:Name="gridViewColumn_IsNeedPassword">
<GridViewColumn.CellTemplate>
<DataTemplate>
<PasswordBox x:Name="passwordBox_PW" MinWidth="100" IsEnabled="{Binding Path=IsNeedPassword}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="gridViewColumn_EntryButton">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Background="Aqua" Click="button_Entry_Click">
<StackPanel Orientation="Horizontal">
<Image Height="Auto" Width="Auto" Source="Resources/login.png"/>
<TextBlock Text="{Binding Converter={StaticResource EntryButtonConverter}}" VerticalAlignment="Center"/>
</StackPanel>
<Button.Tag>
<MultiBinding Converter="{StaticResource EntryButtonTagConverter}">
<Binding Path="ID"/>
<Binding Path="IsNeedPassword"/>
<Binding ElementName="passwordBox_PW" Path="Password"/>
</MultiBinding>
</Button.Tag>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="gridViewColumn_DeleteButton">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Background="Orange" Click="button_Delete_Click" IsEnabled="{Binding Path=Master, Converter={StaticResource DeleteButtonVisibilityConverter}}">
<StackPanel Orientation="Horizontal">
<Image Height="Auto" Width="Auto" Source="Resources/login.png"/>
<TextBlock Text="{Binding Converter={StaticResource DeleteButtonConverter}}" VerticalAlignment="Center"/>
</StackPanel>
<Button.Tag>
<Binding Path="ID"/>
</Button.Tag>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
Something like this.
Now, in the gridViewColumn_EntryButton I need some infos such as RoomID + IsNeedPassword + PasswordText
So i used MultiBinding.
and the EntryButtonTagConverter.Convert is like that.
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string[] result = Array.ConvertAll<object, string>(values, obj =>
{
return (obj == null) ? string.Empty : obj.ToString();
});
// RoomID + IsNeedPassword + PasswordText
return result[0] + '\n' + result[1] + '\n' + result[2];
}
and When i debugging, the result[2], PasswordText is "{DependencyProperty.UnsetValue}"
But i inputed into the PasswordBox asdftest1234.
I don't know why PasswordBox.Password property is not accessable.
Any one some ideas?
Thanks.
It´s not possible to bind directly to the PasswordProperty for security reasons.
Take a look here!
Using PasswordBoxAssistant you can bind password.
public static class PasswordBoxAssistant
{
public static readonly DependencyProperty BoundPasswordProperty =
DependencyProperty.RegisterAttached("BoundPassword", typeof(string)
, typeof(PasswordBoxAssistant), new FrameworkPropertyMetadata(string.Empty, OnBoundPasswordChanged));
public static readonly DependencyProperty BindPasswordProperty = DependencyProperty.RegisterAttached(
"BindPassword", typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged));
private static readonly DependencyProperty UpdatingPasswordProperty =
DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(PasswordBoxAssistant));
private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PasswordBox box = d as PasswordBox;
// only handle this event when the property is attached to a PasswordBox
// and when the BindPassword attached property has been set to true
var ignoreBindProperty = false;
if (box != null && box.Parent != null)
{// TODO: Bind property change not set in case of hosting password box under Telerik datafield. That why I am ignoring the bind propery here - Morshed
ignoreBindProperty = (box.Parent is Telerik.Windows.Controls.DataFormDataField);
}
if (d == null || !(GetBindPassword(d) || ignoreBindProperty))
{
return;
}
// avoid recursive updating by ignoring the box's changed event
box.PasswordChanged -= HandlePasswordChanged;
string newPassword = (string)e.NewValue;
if (!GetUpdatingPassword(box))
{
box.Password = newPassword;
}
box.PasswordChanged += HandlePasswordChanged;
}
private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
// when the BindPassword attached property is set on a PasswordBox,
// start listening to its PasswordChanged event
PasswordBox box = dp as PasswordBox;
if (box == null)
{
return;
}
bool wasBound = (bool)(e.OldValue);
bool needToBind = (bool)(e.NewValue);
if (wasBound)
{
box.PasswordChanged -= HandlePasswordChanged;
}
if (needToBind)
{
box.PasswordChanged += HandlePasswordChanged;
}
}
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox box = sender as PasswordBox;
// set a flag to indicate that we're updating the password
SetUpdatingPassword(box, true);
// push the new password into the BoundPassword property
SetBoundPassword(box, box.Password);
SetUpdatingPassword(box, false);
}
public static void SetBindPassword(DependencyObject dp, bool value)
{
dp.SetValue(BindPasswordProperty, value);
}
public static bool GetBindPassword(DependencyObject dp)
{
return (bool)dp.GetValue(BindPasswordProperty);
}
public static string GetBoundPassword(DependencyObject dp)
{
return (string)dp.GetValue(BoundPasswordProperty);
}
public static void SetBoundPassword(DependencyObject dp, string value)
{
dp.SetValue(BoundPasswordProperty, value);
}
private static bool GetUpdatingPassword(DependencyObject dp)
{
return (bool)dp.GetValue(UpdatingPasswordProperty);
}
private static void SetUpdatingPassword(DependencyObject dp, bool value)
{
dp.SetValue(UpdatingPasswordProperty, value);
}
}
and change in xaml
<PasswordBox helpers:PasswordBoxAssistant.BindPassword="true"
helpers:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true}"/>
Related
I am using Material Design in XAML Toolkit. I have the main window with drawer, which contains the list of user controls (app tabs). When I click on them - application tab switches between this controls. I want to add a button to the window, and when I click on it I want to switch between tabs too. You can see important parts of my code here:
<materialDesign:DialogHost Identifier="RootDialog" SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}">
<materialDesign:DrawerHost IsLeftDrawerOpen="{Binding ElementName=MenuToggleButton, Path=IsChecked}">
<materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel MinWidth="212">
<ToggleButton Style="{StaticResource MaterialDesignHamburgerToggleButton}"
DockPanel.Dock="Top"
HorizontalAlignment="Right" Margin="16"
IsChecked="{Binding ElementName=MenuToggleButton, Path=IsChecked, Mode=TwoWay}" />
<ListBox x:Name="DemoItemsListBox" Margin="0 16 0 16" SelectedIndex="0"
ItemsSource="{Binding DemoItems}"
PreviewMouseLeftButtonUp="UIElement_OnPreviewMouseLeftButtonUp">
<ListBox.ItemTemplate>
<DataTemplate DataType="helpers:DemoItem">
<TextBlock Text="{Binding Name}" Margin="32 0 32 0" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel>
<materialDesign:ColorZone Padding="16" materialDesign:ShadowAssist.ShadowDepth="Depth2"
Mode="PrimaryDark" DockPanel.Dock="Top">
<DockPanel>
<ToggleButton Style="{StaticResource MaterialDesignHamburgerToggleButton}" IsChecked="False"
x:Name="MenuToggleButton"/>
<materialDesign:PopupBox x:Name="popupBox">
<TextBlock>Check me please</TextBlock>
</materialDesign:PopupBox>
<CheckBox x:Name="LizenzeCheckBox" DockPanel.Dock="Right" Style="{StaticResource MaterialDesignCheckBox}" Tag="False">
<CheckBox.IsChecked>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}">
<Binding.ValidationRules>
<helpers:IsCheckedValidationRule />
</Binding.ValidationRules>
</Binding>
</CheckBox.IsChecked>CheckBox text</CheckBox>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="22">My App</TextBlock>
</DockPanel>
</materialDesign:ColorZone>
<Button x:Name="TheBUTTON" Click="Button_Click">Ckicc</Button>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="1"
HorizontalScrollBarVisibility="{Binding ElementName=DemoItemsListBox, Path=SelectedItem.HorizontalScrollBarVisibilityRequirement}"
VerticalScrollBarVisibility="{Binding ElementName=DemoItemsListBox, Path=SelectedItem.VerticalScrollBarVisibilityRequirement}"
Padding="{Binding ElementName=DemoItemsListBox, Path=SelectedItem.MarginRequirement}">
<ContentControl Content="{Binding ElementName=DemoItemsListBox, Path=SelectedItem.Content}" />
</ScrollViewer></Grid>
This is my main window xaml code, as you can see, I bind ListBox Values to DemoItem[] array from viewModel. "TheButton" onclick event is the event which I want to use for tab switching.
My main window view model is:
public class MainWindowViewModel
{
public DemoItem[] DemoItems { get; }
public MainWindowViewModel(ISnackbarMessageQueue snackbarMessageQueue)
{
if (snackbarMessageQueue == null) throw new ArgumentNullException(nameof(snackbarMessageQueue));
DemoItems = new[]
{
new DemoItem("Tab1", new Tab1()),
new DemoItem("Tab2", new Tab2()),
new DemoItem("Tab3", new Tab3()),
};
}
}
The MainWindow.cs is:
public partial class MainWindow : Window
{
public static Snackbar Snackbar;
public MainWindow()
{
InitializeComponent();
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
}).ContinueWith(t =>
{
MainSnackbar.MessageQueue.Enqueue("Welcome to my app");
}, TaskScheduler.FromCurrentSynchronizationContext());
DataContext = new MainWindowViewModel(MainSnackbar.MessageQueue);
}
private void UIElement_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//until we had a StaysOpen glag to Drawer, this will help with scroll bars
var dependencyObject = Mouse.Captured as DependencyObject;
while (dependencyObject != null)
{
if (dependencyObject is ScrollBar) return;
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
}
MenuToggleButton.IsChecked = false;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//what to do here?
}
}
The DemoItem Class is:
public class DemoItem : INotifyPropertyChanged
{
private string _name;
private object _content;
private ScrollBarVisibility _horizontalScrollBarVisibilityRequirement;
private ScrollBarVisibility _verticalScrollBarVisibilityRequirement;
private Thickness _marginRequirement = new Thickness(16);
public DemoItem(string name, object content)
{
_name = name;
Content = content;
}
public string Name
{
get { return _name; }
set { this.MutateVerbose(ref _name, value, RaisePropertyChanged()); }
}
public object Content
{
get { return _content; }
set { this.MutateVerbose(ref _content, value, RaisePropertyChanged()); }
}
public ScrollBarVisibility HorizontalScrollBarVisibilityRequirement
{
get { return _horizontalScrollBarVisibilityRequirement; }
set { this.MutateVerbose(ref _horizontalScrollBarVisibilityRequirement, value, RaisePropertyChanged()); }
}
public ScrollBarVisibility VerticalScrollBarVisibilityRequirement
{
get { return _verticalScrollBarVisibilityRequirement; }
set { this.MutateVerbose(ref _verticalScrollBarVisibilityRequirement, value, RaisePropertyChanged()); }
}
public Thickness MarginRequirement
{
get { return _marginRequirement; }
set { this.MutateVerbose(ref _marginRequirement, value, RaisePropertyChanged()); }
}
public event PropertyChangedEventHandler PropertyChanged;
private Action<PropertyChangedEventArgs> RaisePropertyChanged()
{
return args => PropertyChanged?.Invoke(this, args);
}
}
My MutateVerbose function looks like:
public static void MutateVerbose<TField>(this INotifyPropertyChanged instance, ref TField field, TField newValue, Action<PropertyChangedEventArgs> raise, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<TField>.Default.Equals(field, newValue)) return;
field = newValue;
raise?.Invoke(new PropertyChangedEventArgs(propertyName));
}
I don't know how to switch tabs with button click in this situation. Help me, please!
I want to change the background color of listview alternate rows. I am binding values to listview through ObservableCollection. So that I can't iterate through listview items. It shows:
`System.InvalidCastException: 'Unable to cast object of type 'xx.StudentClass' to type 'Windows.UI.Xaml.Controls.ListViewItem'.'
ObservableCollection<StudentClass> StudentData = new ObservableCollection<StudentClass>();
var statement = connection.Prepare("SELECT name,ID from student_details");
while (!(SQLiteResult.DONE == statement.Step()))
{
if (statement[0] != null)
{
StudentClass c1 = new StudentClass() { studentName= statement[0].ToString, studentID= statement[1].ToString};
StudentData.Add(c1);
}
}
StudentListview.ItemsSource = StudentData;
ChangeBgColor();
private void ChangeBgColor()
{
int counter = 1;
foreach (ListViewItem item in this.StudentListview.Items)
{
if (counter % 2 == 0)
{
item.Background = new SolidColorBrush(Colors.Orange);
}
else
{
item.Background = new SolidColorBrush(Colors.OrangeRed);
}
counter++;
}
}
<ListView x:Name="StudentListview" Visibility="Collapsed" VerticalAlignment="Top" HorizontalAlignment="Right" Height="250px" Width="550px">
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<StackPanel Orientation="Vertical" >
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding studentName}" FontSize="20" Width="350px" TextWrapping="Wrap"></TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding studentID}" FontSize="20" Width="350px" TextWrapping="Wrap" ></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want this to tackle in a future proof way, I would suggest creating a new ListView control that can handle this...
Here is how I did this, first define the new control with following properties
public class AlternatingListView : ListView
{
public static readonly DependencyProperty OddRowBackgroundProperty = DependencyProperty.Register(
nameof(OddRowBackground),
typeof(Brush),
typeof(AlternatingListView),
new PropertyMetadata(null));
public static readonly DependencyProperty EvenRowBackgroundProperty = DependencyProperty.Register(
nameof(EvenRowBackground),
typeof(Brush),
typeof(AlternatingListView),
new PropertyMetadata(null));
public Brush OddRowBackground
{
get { return (Brush)GetValue(OddRowBackgroundProperty); }
set { SetValue(OddRowBackgroundProperty, (Brush)value); }
}
public Brush EvenRowBackground
{
get { return (Brush)GetValue(EvenRowBackgroundProperty); }
set { SetValue(EvenRowBackgroundProperty, (Brush)value); }
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
ListViewItem listViewItem = element as ListViewItem;
if (listViewItem == null)
{
return;
}
int index = IndexFromContainer(element);
listViewItem.Background = (index + 1) % 2 == 1 ? OddRowBackground : EvenRowBackground;
}
}
With all this in place you can add that control your XAML and define the needed colors.
<controls:AlternatingListView x:Name="ListView"
ItemsSource="{x:Bind Items}"
EvenRowBackground="SlateGray"
OddRowBackground="White" />
Getting error : 'Could not find method named 'LostFocus' on object of type 'MyType' that matches the expected signature.'
<DataGridTemplateColumn MinWidth="80" Width="1.25*" Header="6">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<customControlls:NumericTextBox x:Name="cc"
Style="{StaticResource NumericTextboxStyle}"
Text="{Binding AccountsReceivable.OverdueAtTheEndOfTheReportingPeriod, UpdateSourceTrigger=LostFocus}">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="LostFocus" SourceName="cc">
<interactions:CallMethodAction TargetObject="{Binding}" MethodName="LostFocus"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</customControlls:NumericTextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
And the method in ViewModel which I'm trying to call. I also tried to remove parameters from method, still same error.
public void LostFocus(object sender, EventArgs e){}
I got it working. You need to bind TargetObject to DataGrid's DataContext.
<DataGridTemplateColumn MinWidth="80" Width="1.25*" Header="6">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<customControlls:NumericTextBox
Style="{StaticResource NumericTextboxStyle}"
Text="{Binding AccountsReceivable.OverdueAtTheEndOfTheReportingPeriod, UpdateSourceTrigger=LostFocus}">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="LostFocus">
<interactions:CallMethodAction MethodName="LostFocus" TargetObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext}" />
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</customControlls:NumericTextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
And the method signature which will be called should be:
public void LostFocus(object sender, RoutedEventArgs e){}
I had this issue too and instead wrote my own TriggerAction to get rid of the constraint to have a specific method signature. Mind you that this code would have to be improved, in order to be fully viable (method arguments)
public class InvokeMethodAction : Microsoft.Xaml.Behaviors.TriggerAction<DependencyObject>
{
public static readonly DependencyProperty TargetObjectProperty = DependencyProperty.Register(
nameof(TargetObject), typeof(FrameworkElement), typeof(InvokeMethodAction), new PropertyMetadata(default(FrameworkElement)));
public FrameworkElement TargetObject
{
get { return (FrameworkElement) GetValue(TargetObjectProperty); }
set { SetValue(TargetObjectProperty, value); }
}
public static readonly DependencyProperty MethodNameProperty = DependencyProperty.Register(
nameof(MethodName), typeof(string), typeof(InvokeMethodAction), new PropertyMetadata(default(string)));
public string MethodName
{
get { return (string) GetValue(MethodNameProperty); }
set { SetValue(MethodNameProperty, value); }
}
/// <inheritdoc />
protected override void Invoke(object parameter)
{
if (TargetObject != null && MethodName != null)
{
var method = TargetObject.GetType().GetMethod(MethodName);
if (method != null)
{
method.Invoke(TargetObject, null);
}
}
}
}
I have a listView like this :
<ListView Grid.Row="1" x:Name="itemsListView"
BorderBrush="White" HorizontalAlignment="Stretch"
ItemsSource="{Binding Path=Items}"
SelectedItem="{Binding Path=ActualItem}">
<ListView.View>
<GridView>
<GridViewColumn Header="{x:Static p:Resources.STATUS}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<local:StatusElement State="{Binding Path=Status,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
Height="20"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="{x:Static p:Resources.NAME}"
DisplayMemberBinding="{Binding Path=Name,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
</GridView>
</ListView.View>
</ListView>
It binds a list of items named Items with many fields. A thread parse the items and update the fields when it finishes. I call method OnPropertyChanged when fields are updated. It works fine for all fields except the one using my UserControl local:StatusElement. I've try to display my STATUS like the NAME, it's refreshes correctly but with local:StatusElement there is no refresh. breakpoints on get/set for StatusElement.State are never reached.
My userControl :
<UserControl ...
x:Name="mainControl">
<Grid Name="LabelGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Name="MyImage"
Source="{Binding Source, Source={StaticResource MyImage}}"
Width="{Binding Height, ElementName=mainControl}"
Height="{Binding Height, ElementName=mainControl}"/>
<Label Grid.Column="1" Name="statusLabel"/>
</Grid>
</UserControl>
and:
public partial class StatusElement : UserControl
{
// Dependency property backing variables
public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State",
typeof(String), typeof(StatusElement), new UIPropertyMetadata(null));
private string _state = "";
public String State
{
get
{
return _state;
}
set
{
_state = value;
RefreshState();
}
}
private void RefreshState()
{
switch (State)
{
case "":
MyImage.Visibility = Visibility.Hidden;
break;
default:
MyImage.Visibility = Visibility.Visible;
break;
}
statusLabel.Content = State;
}
public StatusElement()
{
InitializeComponent();
RefreshState();
}
}
Why the Content of my statusLabel doesn't refresh ?
Your definition of the State dependency property is wrong.
It has to look like shown below, where the CLR property wrapper must call the GetValue and SetValue methods of the DependencyObject that owns the property.
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
"State",
typeof(string),
typeof(StatusElement),
new PropertyMetadata(null, (o, e) => ((StatusElement)o).RefreshState()));
public string State
{
get { return (string)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
Note the second argument to the PropertyMetadata constructor. It is a static PropertyChangedCallback, implemented as lambda expression.
Your class doesnt implement the INotifyPropertyChanged event. Implement it for the update to happen.
Notifies clients that a property value has changed.
public partial class StatusElement : UserControl,INotifyPropertyChanged
{
....
public event PropertyChangedEventHandler PropertyChanged;
private void RefreshState([CallerMemberName]string prop = "")
{
switch (State)
{
case "":
MyImage.Visibility = Visibility.Hidden;
break;
default:
MyImage.Visibility = Visibility.Visible;
break;
}
statusLabel.Content = State;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
I have a AutoCompleteBox as a DataGrid column type. Like so:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Thing, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<SLToolkit:AutoCompleteBox Text="{Binding Path=Thing,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
However, I want to restrict the user's input to uppercase. On TextBoxes I can do so like the following, but I can't get that to work with the AutoCompleteBoxes.
<DataGridTextColumn Binding="{Binding UpdateSourceTrigger=PropertyChanged, Path=Thing}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="CharacterCasing" Value="Upper" />
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
I've tried this:
<SLToolkit:AutoCompleteBox Text="{Binding Path=Thing,
UpdateSourceTrigger=PropertyChanged}"
TextChanged="AutoComplete_TextChanged" />
With this:
private void AutoComplete_TextChanged(object sender, RoutedEventArgs e)
{
AutoCompleteBox box = sender as AutoCompleteBox;
if (box == null) return;
box.Text = box.Text.ToUpper();
}
That kind of works except that it writes backwards. When the user inputs a character, the cursor goes back to the start of the box so the next word is in front of the previous one. If I wrote 'example', I would see "ELPMAXE".
Any ideas?
I solved a similar problem where I only wanted entry of numbers in a textbox, so I used a behavior. If a non-number is entered, the character is deleted. I also used the interactivity library which uses the System.Windows.Interactivity.dll (just import this DLL into your project, if you don't have it its part of the blend sdk http://www.microsoft.com/en-us/download/details.aspx?id=10801).
Here is the simplified XAML:
<Window x:Class="Sample.SampleWindow"
xmlns:main="clr-namespace:MySampleApp"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Title="Sample"
Height="800"
Width="1025"
>
<Grid>
<TextBox Text="{Binding Path=Entry, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="30"
MaxLength="4"
HorizontalAlignment="Left">
<i:Interaction.Behaviors>
<main:KeyPressesWithArgsBehavior
KeyUpCommand="{Binding KeyUpFilterForUpperCaseSymbols}" />
</i:Interaction.Behaviors>
</TextBox>
</Grid>
</Window>
Uses the following Behavior class:
public class KeyPressesWithArgsBehavior : Behavior<UIElement>
{
#region KeyDown Press DependencyProperty
public ICommand KeyDownCommand
{
get { return (ICommand) GetValue(KeyDownCommandProperty); }
set { SetValue(KeyDownCommandProperty, value); }
}
public static readonly DependencyProperty KeyDownCommandProperty =
DependencyProperty.Register("KeyDownCommand", typeof (ICommand), typeof (KeyPressesWithArgsBehavior));
#endregion KeyDown Press DependencyProperty
#region KeyUp Press DependencyProperty
public ICommand KeyUpCommand
{
get { return (ICommand) GetValue(KeyUpCommandProperty); }
set { SetValue(KeyUpCommandProperty, value);}
}
public static readonly DependencyProperty KeyUpCommandProperty =
DependencyProperty.Register("KeyUpCommand", typeof(ICommand), typeof (KeyPressesWithArgsBehavior));
#endregion KeyUp Press DependencyProperty
protected override void OnAttached()
{
AssociatedObject.KeyDown += new KeyEventHandler(AssociatedUIElementKeyDown);
AssociatedObject.KeyUp += new KeyEventHandler(AssociatedUIElementKeyUp);
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.KeyDown -= new KeyEventHandler(AssociatedUIElementKeyDown);
AssociatedObject.KeyUp -= new KeyEventHandler(AssociatedUIElementKeyUp);
base.OnDetaching();
}
private void AssociatedUIElementKeyDown(object sender, KeyEventArgs e)
{
if (KeyDownCommand != null)
{
ObjectAndArgs oa = new ObjectAndArgs {Args = e, Object = AssociatedObject};
KeyDownCommand.Execute(oa);
}
}
private void AssociatedUIElementKeyUp(object sender, KeyEventArgs e)
{
if (KeyUpCommand != null)
{
KeyUpCommand.Execute(AssociatedObject);
}
}
}
Then in your View Model you can implement the command.
SampleWindowViewModel.cs:
public ICommand KeyUpFilterForUpperCaseSymbolsCommand
{
get
{
if (_keyUpFilterForUpperCaseSymbolsCommand== null)
{
_keyUpFilterForUpperCaseSymbolsCommand= new RelayCommand(KeyUpFilterForUpperCaseSymbols);
}
return _keyUpFilterForUpperCaseSymbolsCommand;
}
}
...
private void KeyUpFilterForUpperCaseSymbols(object sender)
{
TextBox tb = sender as TextBox;
if (tb is TextBox)
{
// check for a lowercase character here
// then modify tb.Text, to exclude that character.
// Example: tb.Text = oldText.Substring(0, x);
}
}