I've extended an the AutoCompleteBox of Silverlight and overridden the OnDropDownClosed event handler. This works as expected except that the component looses the focus to the Browser once the DropDown is closed.
What do I have to change in order to keep it?
Here's my code:
namespace ITPole.Sphere.Application.Core.Controls
{
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
public class CustomCompleteBox : AutoCompleteBox
{
public static readonly DependencyProperty SelectedAtCloseProperty =
DependencyProperty.Register(
"SelectedAtClose", typeof(object), typeof(CustomCompleteBox), new PropertyMetadata(null));
public object SelectedAtClose
{
get
{
return this.GetValue(SelectedAtCloseProperty);
}
set
{
this.SetValue(SelectedAtCloseProperty, value);
}
}
protected override void OnDropDownClosed(RoutedPropertyChangedEventArgs<bool> e)
{
base.OnDropDownClosed(e);
this.SelectedAtClose = this.SelectedItem;
}
protected override void OnTextChanged(RoutedEventArgs e)
{
base.OnTextChanged(e);
if (string.IsNullOrEmpty(this.Text))
{
this.SetValue(SelectedAtCloseProperty, null);
}
}
}
}
And the usage in xaml:
<Controls1:CustomCompleteBox x:Name="portfolioAutoCompleteBox"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="1"
Margin="2"
DataContext="{Binding Portfolio}"
Style="{StaticResource DefaultAutoCompleteBoxStyle}"
ItemTemplate="{StaticResource DescriptionItemTemplate}"
ValueMemberBinding="{Binding Description, Mode=TwoWay}"
SelectedAtClose="{Binding Value, ValidatesOnDataErrors=True, Mode=TwoWay}"
ItemsSource="{Binding Values}"
Text="{Binding Text, Mode=TwoWay}"
Behaviors:AutoCompleteBoxBehaviors.PopulatingCommand="{Binding PopulationCommand}" />
What version of Silverlight are you using? It works for me with V4.
Related
I have this Custom Control
XAML:
<UserControl x:Class="WpfApplication1.UC"
...
x:Name="uc">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<TextBox Text="{Binding Test, ElementName=uc}" Width="50" HorizontalAlignment="Left"/>
</StackPanel>
</UserControl>
C#
public partial class UC : UserControl
{
public static readonly DependencyProperty TestProperty;
public string Test
{
get
{
return (string)GetValue(TestProperty);
}
set
{
SetValue(TestProperty, value);
}
}
static UC()
{
TestProperty = DependencyProperty.Register("Test",typeof(string),
typeof(UC), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public UC()
{
InitializeComponent();
}
}
And this is how i used that custom control:
<DockPanel>
<ItemsControl ItemsSource="{Binding Path=DataList}"
DockPanel.Dock="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" CommandParameter="{Binding}" Click="Button_Click"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<local:UC Test="{Binding SelectedString, Mode=OneWay}"/>
</DockPanel>
--
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<string> _dataList;
public ObservableCollection<string> DataList
{
get { return _dataList; }
set
{
_dataList = value;
OnPropertyChanged("DataList");
}
}
private string _selectedString;
public string SelectedString
{
get { return _selectedString; }
set
{
_selectedString = value;
OnPropertyChanged("SelectedString");
}
}
public MainWindow()
{
InitializeComponent();
this.DataList = new ObservableCollection<string>();
this.DataList.Add("1111");
this.DataList.Add("2222");
this.DataList.Add("3333");
this.DataList.Add("4444");
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SelectedString = (sender as Button).CommandParameter.ToString();
}
}
If I do not change text of UC, everything is ok. When I click each button in the left panel, button's content is displayed on UC.
But when I change text of UC (ex: to 9999), Test property lost binding. When I click each button in the left panel, text of UC is the same that was changed (9999). In debug I see that SelectedString is changed by each button click but UC's text is not.
I can 'fix' this problem by using this <TextBox Text="{Binding Test, ElementName=uc, Mode=OneWay}" Width="50" HorizontalAlignment="Left"/> in the UC.
But I just want to understand the problem, can someone help me to explain it please.
Setting the value of the target of a OneWay binding clears the binding. The binding <TextBox Text="{Binding Test, ElementName=uc}" is two way, and when the text changes it updates the Test property as well. But the Test property is the Target of a OneWay binding, and that binding is cleared.
Your 'fix' works because as a OneWay binding, it never updates Test and the binding is never cleared. Depending on what you want, you could also change the UC binding to <local:UC Test="{Binding SelectedString, Mode=TwoWay}"/> Two Way bindings are not cleared when the source or target is updated through another method.
The issue is with below line
<local:UC Test="{Binding SelectedString, Mode=OneWay}"/>
The mode is set as oneway for SelectString binding so text will be updated when the value from code base changes. To change either the source property or the target property to automatically update the binding source as TwoWay.
<local:UC Test="{Binding SelectedString, Mode=TwoWay}"/>
I'd like to switch the tabs using the Tab keys instead of standard Ctrl+Tab
My code is
<TabControl>
<TabItem Header="Section 1" Name="tabSection1">
<ScrollViewer>
<ContentPresenter Name="cntSection1" />
</ScrollViewer>
</TabItem>
<TabItem Header="Section 2" Name="tabSection2">
<ScrollViewer>
<ContentPresenter Name="cntSection2" />
</ScrollViewer>
</TabItem>
</TabControl>
<StackPanel>
<Button Content="Save" Name="btnSave" />
<Button Content="Cancel" Name="btnCancel" IsCancel="True" />
</StackPanel>
Each of my ContentPresenters contains UserControls with multiple UI Elements such as Textboxes and Checkboxes.
So far I have tried the follow with no luck.
<TabControl.InputBindings>
<KeyBinding Key="Tab" Modifiers="Control" Command="EditingCommands.TabForward" />
</TabControl.InputBindings>
And
<TabControl KeyboardNavigation.TabNavigation="Continue" KeyboardNavigation.ControlTabNavigation="None">
I implemented something similar where I wanted the tab key to change tabs when the last textbox in the tab was currently focused. I used the following Attached Behavior:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Core.Common
{
public static class ChangeTabsBehavior
{
public static bool GetChangeTabs(DependencyObject obj)
{
return (bool)obj.GetValue(ChangeTabsBehaviorProperty);
}
public static void SetChangeTabs(DependencyObject obj, bool value)
{
obj.SetValue(ChangeTabsBehaviorProperty, value);
}
public static readonly DependencyProperty ChangeTabsBehaviorProperty =
DependencyProperty.RegisterAttached("ChangeTabs",
typeof(bool), typeof(ChangeTabsBehavior),
new PropertyMetadata(false, OnChangeTabsChanged));
private static void OnChangeTabsChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var textBox = (FrameworkElement) sender;
var changeTabs = (bool) (e.NewValue);
if (changeTabs)
textBox.PreviewKeyDown += TextBoxOnPreviewKeyDown;
else
textBox.PreviewKeyDown -= TextBoxOnPreviewKeyDown;
}
private static void TextBoxOnPreviewKeyDown(object sender, KeyEventArgs keyEventArgs)
{
if (keyEventArgs.Key == Key.Tab)
{
var textBox = (FrameworkElement) sender;
var tabControl = textBox.TryFindParent<TabControl>();
if (tabControl.SelectedIndex == tabControl.Items.Count - 1)
tabControl.SelectedIndex = 0;
else
tabControl.SelectedIndex++;
keyEventArgs.Handled = true;
}
}
}
}
See http://www.hardcodet.net/2008/02/find-wpf-parent for the TryFindParent method
You then attach the behavior in XAML like so:
<TextBox local:ChangeTabsBehavior.ChangeTabs="True"/>
You should be able to modify this pretty easily for your needs.
More on behaviors here: http://www.jayway.com/2013/03/20/behaviors-in-wpf-introduction/
I have a combobox that is editable by the user, so I bound the Text property to a property of my class. The ItemsSource of that same combobox is bound to an AsyncObservableCollection (which I did based on other posts and it works nicely).
However, I have a problem when updating the ItemsSource.
Here's the Steps to reproduce:
Select a value in the combobox drop down.
Type some text into the combobox. (say "aaa")
Update the ItemsSource. (via my button click)
Result: The MyText property remains set to the text you typed in ("aaa"), but the combo box shows a blank entry.
However, if you do the same steps above but skip Step 1, the combobox shows the text from the MyText property correctly. This leads me to believe that the selected index/selected value is being used to update the combobox after the update to the ItemsSource is done.
Any ideas on how I can keep the displayed value in sync with the MyText property after an update to the ItemsSource?
In the code provided below I'm updating the ItemsSource on the button click in order to reproduce.
Thank You!
XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="comboBox1" VerticalAlignment="Top" Width="200" IsEditable="True"
DataContext="{Binding Path=MyDataClass}"
ItemsSource="{Binding Path=MyListOptions}"
SelectedIndex="{Binding Path=MySelectedIndex}"
Text="{Binding Path=MyText, UpdateSourceTrigger=LostFocus}"
>
</ComboBox>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="416,276,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
Code behind:
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Diagnostics;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class DataClass : INotifyPropertyChanged
{
private string mytext = "";
public string MyText
{
get
{
return mytext;
}
set
{
mytext = value;
OnPropertyChanged("MyText");
}
}
private int myselectedindex = -1;
public int MySelectedIndex
{
get
{
return myselectedindex;
}
set
{
if (value != -1)
{
mytext = MyListOptions[value];
OnPropertyChanged("MyText");
}
}
}
private AsyncObservableCollection<string> mylistOptions = new AsyncObservableCollection<string>();
public AsyncObservableCollection<string> MyListOptions
{
get
{
return mylistOptions;
}
set
{
mylistOptions.Clear();
OnPropertyChanged("MyListOptions");
foreach (string opt in value)
{
mylistOptions.Add(opt);
}
OnPropertyChanged("MyListOptions");
}
}
public DataClass()
{
}
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPropertyChanged(string prop)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public DataClass MyDataClass { get; set; }
public MainWindow()
{
MyDataClass = new DataClass();
MyDataClass.MyListOptions.Add("Option 1 - Provides helpful stuff.");
MyDataClass.MyListOptions.Add("Option 2 - Provides more helpful stuff.");
MyDataClass.MyListOptions.Add("Option 3 - Provides extra helpful stuff.");
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
private void button1_Click(object sender, RoutedEventArgs e)
{
AsyncObservableCollection<string> newList = new AsyncObservableCollection<string>();
newList.Add("Option A - Provides helpful stuff.");
newList.Add("Option B - Provides more helpful stuff.");
newList.Add("Option C - Provides extra helpful stuff.");
MyDataClass.MyListOptions = newList;
}
}
}
Ok, I solved this problem by binding the SelectedValue to the same property as Text and setting its mode to OneWay.
<ComboBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="comboBox1" VerticalAlignment="Top" Width="200" IsEditable="True"
DataContext="{Binding Path=MyDataClass}"
ItemsSource="{Binding Path=MyListOptions}"
SelectedIndex="{Binding Path=MySelectedIndex}"
SelectedValue="{Binding Path=MyText, Mode=OneWay}"
Text="{Binding Path=MyText, UpdateSourceTrigger=LostFocus}"
I need to display a image on mouse over only in silverlight 5.
Can any one please help me.
Give me any idea how to achieve it...
<sdk:DataGridTemplateColumn x:Name="colDeleteContent" IsReadOnly="True" Header="Delete Content" Width="100" CanUserResize="False">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel x:Name="spDeleteContent" VerticalAlignment="Center" Margin="10,0,0,0" Width="20" Height="20" HorizontalAlignment="Center" Orientation="Vertical">
<Image x:Name="imgDeleteContent" Source="Assets/Images/close.png" Height="15" Width="15" Margin="0" MouseLeftButtonDown="imgDeleteContent_MouseLeftButtonDown" Cursor="Hand"/>
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
Neon
There are many ways, OnFocus set your images Visibility Visible and on FocusLeft set Collapsed basically of your main element.
But I see that it is on DataTemplate on your sample.
So There are some ways I imagine.
1)Create a new component instead of element in DataTemplate such as
namespace ProjectBus
{
public class StackPanelHasHiddenImage : Control
{
//You may don't need dependency property
//It supports bindability
#region dependency property
public static Image GetMyProperty(DependencyObject obj)
{
return (Image)obj.GetValue(ImageProperty);
}
public static void SetMyProperty(DependencyObject obj, Image value)
{
obj.SetValue(ImageProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached("Image", typeof(Image), typeof(StackPanelHasHiddenImage), new System.Windows.PropertyMetadata(null));
#endregion
public Image Image
{
get;
set;
}
protected override void OnGotFocus(RoutedEventArgs e)
{
Image.Visibility = Visibility.Visible;
base.OnGotFocus(e);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
Image.Visibility = Visibility.Collapsed;
base.OnLostFocus(e);
}
}
}
Then in your xaml use like
<DataTemplate>
<local:StackPanelHasHiddenImage Image="/ProjectBus;component/blabal.png"/>
</DataTemplate>
2) Use GotoStateAction behaviour
http://msdn.microsoft.com/en-us/library/ff723953%28v=expression.40%29.aspx but I see that its in a DataTemplate and using this may not be easier.
3) MainElement.FinChildByType < StackPanel >().FirstOrDefault() is not null then add your focus and unfocus handler to this element on your codebehind. But this is a method I mostly avoid to use.
Its a bit harder because its in a template so your named object in template can't be seen on your codebehind.
Hope helps
I have a Silverlight combo box outside of a grid which works fine.
However, I cannot get it to work properly inside the data grid. I am not certain what I am doing incorrectly. Help with this is greatly appreciated!
This code works fine for the silverlight combo box outside of the grid:
XAML:
<ComboBox Height="23" HorizontalAlignment="Left" ItemsSource="{Binding ElementName=comboBoxItemDomainDataSource, Path=Data}" Margin="112,72,0,0" Name="comboBoxItemComboBox" VerticalAlignment="Top" Width="185" SelectionChanged="comboBoxItemComboBox_SelectionChanged" DisplayMemberPath="ComboDisplayValue">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</Grid>
<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:ComboBoxItem, CreateList=true}" Height="0" LoadedData="comboBoxItemDomainDataSource_LoadedData" Name="comboBoxItemDomainDataSource" QueryName="GetComboboxItems_PatIdEssentrisQuery" Width="0">
<riaControls:DomainDataSource.DomainContext>
<my:ComboBoxItemContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Combo Box Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace CorporateHR.Web
{
public class ComboBoxItem
{
[Key]
public int ComboID_Int { get; set; }
public string ComboDisplayValue { get; set; }
private static List<ComboBoxItem> GetComboBoxItems(string strStoredProcedure)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["RefConnectionString"].ConnectionString);
SqlCommand cmd = new SqlCommand(strStoredProcedure, con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
List<ComboBoxItem> comboList = new List<ComboBoxItem>();
con.Open();
SqlDataReader dr = cmd.ExecuteReader(behavior: CommandBehavior.CloseConnection);
while (dr.Read())
{
ComboBoxItem ComboBoxItem = new ComboBoxItem();
ComboBoxItem.ComboID_Int = Convert.ToInt32(dr[0].ToString());
ComboBoxItem.ComboDisplayValue = dr[1].ToString();
comboList.Add(ComboBoxItem);
}
return comboList;
}
public static List<ComboBoxItem> GetComboboxItems_PatIdEssentris()
{
return GetComboBoxItems("uspLookupPatIdEssentris");
}
//Secondary ComboBox Lookup:
public static List<ComboBoxItem> GetComboboxItems_ORStatus()
{
return GetComboBoxItems("uspLookupORStatus");
}
}
}
Combo Box Domain Service:
namespace CorporateHR.Web
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
// TODO: Create methods containing your application logic.
[EnableClientAccess()]
public class ComboBoxItemService : DomainService
{
public IEnumerable<ComboBoxItem> GetComboboxItems_PatIdEssentris()
{
return ComboBoxItem.GetComboboxItems_PatIdEssentris();
}
public IEnumerable<ComboBoxItem> GetComboboxItems_ORStatus()
{
return ComboBoxItem.GetComboboxItems_ORStatus();
}
}
}
Code Behind Page (for combo box which populates):
private void comboBoxItemDomainDataSource_LoadedData(object sender, LoadedDataEventArgs e)
{
if (e.HasError)
{
System.Windows.MessageBox.Show(e.Error.ToString(), "Load Error", System.Windows.MessageBoxButton.OK);
e.MarkErrorAsHandled();
}
}
private void comboBoxItemComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
I used a templated column like:
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="2" VerticalAlignment="Center" HorizontalAlignment="Left"
Text="{Binding Path=Option0, Mode=OneWay}" Width="Auto" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Height="23" Name="cbx0" SelectedValuePath="Display" DisplayMemberPath="Display"
SelectedValue="{Binding Path=Option0, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource DataContextProxy},Path=DataSource.ocList0}"
MinWidth="65"
Width="Auto">
</ComboBox>
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
Where _ocList0 is of type ObservableCollection<cComboBoxOption>
And this is the cComboBoxOption class:
public class cComboBoxOption
{
public int Id { get; set; }
public string Display { get; set; }
public cComboBoxOption(int id, string name)
{
this.Id = id;
this.Display = name;
}
}
This was written in a generic way because I didn't know what the bindings would be or what the combo box would contain until run time.
A simpler way to do this is to use List<string> see the blog post HERE.