I'm experiencing som strange behaviour by the Silverlight ComboBox. I started out with some simple code:
xaml:
<ComboBox Name="drpInstallation" SelectionChanged="drpInstallation_SelectionChanged" />
cs:
List<string> installations = new List<string>();
installations.Add("Testing 123");
installations.Add("Antoher test");
installations.Add("Yeah");
drpInstallation.ItemsSource = installations;
Everything works well when clicking an item. However, if I use the ItemTemplate in ComboBox like this:
xaml:
<ComboBox Name="drpInstallation" SelectionChanged="drpInstallation_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<ComboBoxItem Content="{Binding Installation}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
cs:
ICollection<InstallationClass> installations = a list of the installation class;
drpInstallation.ItemsSource = installations;
InstallationClass.cs:
public class InstallationClass
{
public int PK;
public string Installation;
}
Now the ComboBox displays correctly, however when I click the text if the items nothing happens. If I click just right of the text itself then the item is selected like normal. Point is; the natural thing to do is to click the text itself, not to the left or right of it. Any idea why this happens, and any idea how to correct it? Is this a Silverlight bug ?
Your DataTemplate should look like this:
<ComboBox Name="drpInstallation" SelectionChanged="drpInstallation_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Installation}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The problem was that ComboBoxItems consume the click event, rather than bubbling it up.
Related
I'm binding a List<string> to my ListBox in WPF using MVVM
At the moment I have
<ListBox ItemsSource="{Binding FileContents}"></ListBox>
File Contents in my ViewModel is simply
public List<string> FileContents {get;set;}
And the FileContents values are set in the constructor of the ViewModel, as such there is no need to worry about INotifyProperty
Everything works fine so far. I can see the list displayed in my ListBox as desired.
Now I need to provide a template! This is where it goes wrong
<ListBox ItemsSource="{Binding FileContents}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is where it all goes wrong! My understanding is that I only need to do <TextBox Text = "{Binding}" because the ListBox is already bound to the List<string> property (called FileContents)
However, when I run the above Visual Studio gives me
The application is in break mode
If I update the code to
<TextBox Text = "Some String Value"
then it works fine
I don't understand what I've done wrong.
Set the Mode of the Binding to OneWay:
<TextBox Text="{Binding Path=., Mode=OneWay}" />
The default binding mode for the Text property of a TextBox is TwoWay but this won't work when you bind to a string in a List<string>.
Binding to a string directly is only possible one way. This means you are only able to bind read only like
<TextBox Text="{Binding Mode=OneWay}"/>
or
<TextBox Text="{Binding .}"/>
The reason is simple: Changing the string means you are removing and adding an item to your list. This is simply not possible by changing the string in a TextBox.
A solution is to wrap the content in a class like
public class FileContent
{
public string Content { get; set; }
}
and bind to a list of List<FileContent> by using <TextBox Text="{Binding Content}"/> as template.
I have an ObservableCollectiong<StringWrapper> (StringWrapper per this post) named Paragraphs bound to an ItemsControl whose ItemTemplate is just a TextBox bound to StringWrapper.Text.
XAML
<ItemsControl Name="icParagraphs" Grid.Column="1" Grid.Row="7" ItemsSource="{Binding Path=Paragraphs, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<StackPanel Orientation="Vertical">
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Name="tbParagraph" TextWrapping="Wrap" AcceptsReturn="False" Text="{Binding Path=Text}" Grid.Column="0" KeyUp="tbParagraph_KeyUp" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C#
public class StringWrapper
{
public string Text { get; set; }
public StringWrapper()
{
Text = string.Empty;
}
public StringWrapper(string text)
{
Text = text;
}
}
I'm trying to make it so when I press enter in a TextBox, I insert a StringWrapper in my ObservableCollection after the StringWrapper bound to the TextBox that's currently focused, which generates a new TextBox. So far, my code does this, though there are a couple glitches to work out.
My question is, how do I then set the focus to the newly generated TextBox? As far as I can tell, the control generation happens after the function that inserts the string returns.
I looked for something like an ItemsControl.ItemsSourceChanged event, but, at least, that name doesn't exist. I also tried attaching a handler to ObservableCollection.CollectionChanged, but that too seemed to fire before the TextBox was generated. Last, since the ItemsControl.Template is a StackPanel, I looked for a StackPanel.ControlAdded event, but couldn't find that either.
Ideas? Thanks!
You may have to handle CollectionChanged and then schedule the focus action to occur in the future using Dispatcher.BeginInvoke with a priority of Loaded. That should give the ItemsControl an opportunity to generate a container and perform layout.
I have a ListView that looks like this, that controls which tab in my application that is opened.
<ListView Grid.ColumnSpan="2" Grid.Row="1" SelectedItem="{Binding Path=SelectedSubstanceName}" Name="listView" ItemsSource="{Binding Path=Substances}" HorizontalAlignment="Stretch" Margin="2" VerticalAlignment="Stretch">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Lägg till" Command="{Binding AddSubstanceCommand}"/>
<MenuItem Header="Ta bort" Command="{Binding RemoveSubstanceCommand}"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I use the SelectedSubstanceName property to detect which tab to open, or switch to, if it's already open.
The property looks like this:
private SubstanceName selectedSubstanceName;
public SubstanceName SelectedSubstanceName
{
get
{
return selectedSubstanceName;
}
set
{
selectedSubstanceName = value;
OnPropertyChanged("SelectedSubstanceName");
if (selectedSubstanceName != null)
{
if (!Tabs.Any(t => t.Identify(selectedSubstanceName.SubstanceNameID, typeof(SubstanceTabsViewModel))))
AddTab(selectedSubstanceName);
else
SelectedTab = Tabs.First(t => t.Identify(selectedSubstanceName.SubstanceNameID, typeof(SubstanceTabsViewModel)));
}
}
}
The case I'm not able to cover is when the user clicks "someSubstance", the corresponding tab is opened, the user closes it, and "someSubstance" is still selected. If the user wants to open it again, he has to select some other substance (which will then be opened), and then click "someSubstance" again. Is it possible to trigger the property even when clicking the same ListViewItem?
I know I could add an event on double-click, but ideally, I want to avoid both events and double-clicks.
I think the problem is that after clicking an item the first time the list's SelectedItem gets set. After clicking the same item the second time SelectedItem won't change because it is already set to that item. What you should do is set the SelectedItem to null after handling the click.
Try to unselect all Items in your ListView after the tab is closed.
YOURLISTVIEW.UnselectAll();
So the next time someone selects an Item there will be a change.
You don't actually want to use the ListView class, but instead simply use the ItemsControl, since it is the most basic way of representing a sequence of elements, but without the extras such as SelectedItem, SelectedValue, etc. that any class deriving from Selector has.
From there, it's merely a matter of how to represent each item in the ItemsControl. The behavior you want is to know when a specific item has been clicked on, which would make the Button class a good candidate, since it handles click behavior through an ICommand interface. Obviously, since you know about DataTemplates and styling in general, you should already know that you can customize how the button looks (visually) without sacrificing the actual behavior (click-handling).
<ItemsControl ItemsSource="{Binding Path=Substances}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource SomeStyleToChangeItsLook}"
Command="{Binding Path=SelectSubstanceCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public ICommand SelectSubstanceCommand { get; private set; }
private void SelectSubstance(object parameter)
{
// Add the substance that was "clicked" on here however you want to do it.
}
Keep in mind I don't know what framework you are using, so I just gave a general example of how the Command code might look in your view-model. The key to MVVM and using WPFs awesome UI is to always think of what behavior you want and which controls offer that behavior. Ignore how they actually look because that can be changed without losing that behavior.
EDIT:
The Answer I was looking for was....
<dataFormToolkit:DataField Label="Business Type:">
<ComboBox x:Name="BusinessType" SelectedItem="{Binding BusinessType, Mode=TwoWay}" >
<ComboBox.Items>
<sys:String>Land</sys:String>
<sys:String>Maritime</sys:String>
</ComboBox.Items>
</ComboBox>
</dataFormToolkit:DataField>
Heres the link to the article
I have a c# silverlight business application that uses the ado.net entity framework and the domain service class to bind to my sql server database and pull data from/ persist data to my database. I have been using the dataformtoolkit namespace to layout textboxes which can be edited/display data, using a TwoWay binding mode in order to allow the read/write functionality.
On some of the fields I want to use a combobox instead of a textbox in order to add a better user experience to my application. The impression i've got from reading around the web is that this isnt as easy as it should be.
My current textbox code looks like:
<dataFormToolkit:DataField>
<TextBox Text="{Binding BusinessType, Mode=TwoWay}" />
</dataFormToolkit:DataField>
my attempt at something similar is as follows but incorrect:
<ComboBox>
<ComboBox.Items>
<ComboBoxItem Content="Maritime" IsSelected="{Binding BusinessType, Mode=TwoWay}" />
<ComboBoxItem Content="Land" IsSelected="{Binding BusinessType, Mode=TwoWay}" />
</ComboBox.Items>
</ComboBox>
NB/ I want the combobox to be populated by a list or an enum etc. (preferably a list). The contents of the combobox should have nothing to do with the database, just that when the user hits submit the selection made in the combobox gets persisted back to the database. It is also important that the combobox can read from the database and display the specific selection that has already been made if this is the case.
****EDIT:
Current setup of dataform bound to datagrid with editable businesstype field as a textbox (I want to replace this textbox with a combobox that has two selectable items).
<!--DataForm Declaration-->
<dataFormToolkit:DataForm x:Name="dataForm1" Height="410" Width="331"
VerticalAlignment="Top"
Header="Job Details"
CurrentItem="{Binding SelectedItem, ElementName=dataGrid1}"
HorizontalAlignment="Left" >
<dataFormToolkit:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<dataFormToolkit:DataField>
<TextBox Text="{Binding BusinessType, Mode=TwoWay}" />
</dataFormToolkit:DataField>
</StackPanel>
</DataTemplate>
</dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>
So how do i manipulate this code to use a combobox instead of a textbox?
Any help in doing this would be greatly appreciated.
You should setup your binding to use the ComboBox's SelectedValue property.
<ComboBox SelectedValue="{Binding BusinessType, Mode=TwoWay}">...</ComboBox>
The problem with this is that the ListBox and ComboBox will use the Equals() method on the object in the SelectedItem so if the types do not match then the ComboBox will not set the appropriate item as selected. Therefore, BusinessType will need to be a string since you are using ComboBoxItem's and specifying string content.
If bound the ItemsSource of the ComboBox then you would use SelectedItem and it would actually be an entity type as well, in which case you have more flexability/control around what equals what.
This is really peculiar. I tried a simple data binding example program. I tried to bind a collection (IList) to a list box. When i alter the collection, the list box is updated only if i maximize the window. Here are the snippets,
<ListBox x:Name="myBirthdaysListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<UniformGrid>
<Label Content="{Binding Name}"></Label>
<Label Content="{Binding DateOfBirth}"></Label>
</UniformGrid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public Window1()
{
InitializeComponent();
myCalendar = new List<Calendar>();
myBirthdaysListBox.DataContext = myCalendar;
}
I am just a beginner in wpf. Kindly let me know, if i have done some thing terribly wrong in here.
Try using a BindingList<Calendar> rather than just a List<Calendar>, As binding list raises events for when items get added/removed/etc.
What you're seeing is the control is redrawing when you resize, and it's going through all the data again.
Off the top of my head, I believe you need to implement INotifyPropertyChanged on your Calendar such that the binding list is notified if an item in it changes