Custom Control in Silverlight ListBox - silverlight

I have a custom control I created from a expression design I created and exported to xaml. I have put in it a bound itemtemplate/datatemplate of a ListBox contorl. It doesn't seem to be rendering more than once and/or it is rendering each item in the same place(kind of like the same x,y coordinates.
It would seem to me that this should be a simple process. If I fill the datatemplate with a textblock it would generate a couple textblocks in a vertical list. I would expect if I swap out the textblock with my custom control I should get a couple custom controls in a vertical list.
Wouldn't this be teh expected behavior or is there a reason the listbox only appears to be rendering a single usercontrol? In both cases I use the same data for the listbox.
<telerik:ListBox x:Name="PeopleList" Grid.Row="1" >
<telerik:ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<custom:ExecSelector Height="100" Width="100" x:Name="ExecSelector" FullName="{Binding City}"></custom:ExecSelector>
</Grid>
</DataTemplate>
</telerik:ListBox.ItemTemplate>
</telerik:ListBox>
People = new List<PersonViewModel>();
PersonViewModel person2 = new PersonViewModel()
{
Name = "Austin Weise",
City = "Texas",
Email = "austin#build1.ca",
Position = "Techincal Director",
Bio = "Programmer"
};
PersonViewModel person = new PersonViewModel()
{
Name = "Ian House",
City = "Vancouver",
Email = "Ian#build1.ca",
Position = "Creative Director",
Bio = "Designer"
};
People.Add(person2);
People.Add(person);
PeopleList.DataContext = this;
PeopleList.ItemsSource = People;
That should provide enough to visualize it unless the UI elements are required for the custom control.

Based on your current description of your problem:
make sure you have not put any values on the Top and Left properties of the outer control.
make sure you have more than one data item in the list you are binding to
make sure your ListBox has sufficient height to be able to show more than one item
make sure you haven't disabled the ListBox scroll bar.
That's all the guesses that i can throw out there unless you have more details.

Related

WPF hit testing a rectangular area

I have a WrapPanel containing an arbitrary number of jagged sized elements. I'd like to implement drag select for my items.
It seems pretty obvious how to HitTest for a point, but how can I find all items within a rectangular area?
You may use VisualTreeHelper.HitTest with a GeometryHitTestParameters argument and a HitTestFilterCallback that checks if a Visual is a direct child of the Panel.
Something like this:
var selectedElements = new List<DependencyObject>();
var rect = new RectangleGeometry(...);
var hitTestParams = new GeometryHitTestParameters(rect);
var resultCallback = new HitTestResultCallback(
result => HitTestResultBehavior.Continue);
var filterCallback = new HitTestFilterCallback(
element =>
{
if (VisualTreeHelper.GetParent(element) == panel)
{
selectedElements.Add(element);
}
return HitTestFilterBehavior.Continue;
});
VisualTreeHelper.HitTest(
panel, filterCallback, resultCallback, hitTestParams);
It looks a little complicated, but the HitTestFilterCallback is necessary to get all Visuals in the visual tree, not only those that actually got hit. For example if your panel contains Label controls, the HitTestResultCallback will only be called for the Border and TextBlock child Visuals of each Label.
The option for controlling hit test visibility is the IsHitTestVisible property. This property allows you to control hit test visibility regardless of the brush with which the UIElement is rendered.
Also, You want to set the Fill to Transperent
<Rectangle Width="200" Height="200" Margin="170,23,12,35" Fill="Transparent" IsHitTestVisible="True" />

Combo-box loses selection after collection changes

I have a WPF combo box bound to an obvserable collection (OC):
<ComboBox Name="cbCombination" ItemsSource="{Binding Combinations}"
SelectedIndex="0" />
Elsewhere, in the object set as data context:
public ObservableCollection<Combination> Combinations { get; set; }
Combination overrides its ToString and everything is peachy: The combo-box's drop-down displays all Combination items in the Combinations OC. The combo-box's selection box displays the value of the first Combination.
Now, the data-context object has to change the values in its Combinations OC:
var combinationsList = CombinationsManager.CombinationsFor(someParam);
this.Combinations.Clear();
foreach (var combination in combinationsList)
this.Combinations.Add(combination);
NotifyPropertyChanged(#"Combinations");
This causes the combo-box's selection box shows an empty string. (The drop-down is closed. However, when I make it drop down, it does show the correct new Combinations, so it is bound to the updated collection).
I tried to capture both SourceUpdated and (in my dispair) TargetUpdated events (thining of setting the SelectedIndex there), but my event handlers didn't get called!
So my question is: How do I make a WPF ComboBox refresh the value of its selection-box when the observable collection it is bound-to changes?
Update:
I've totally forgotten to mention, and I don't know whether it's important, but the combo-box is within a UserControl. The UserControl's XAML looks like this:
<UserControl x:Class="...CombinationsToolBar"
.... mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ToolBarTray Name="toolBarTray1" >
<ToolBar Name="toolBar1">
<ComboBox Name="cbCombination"
ItemsSource="{Binding Path=Combinations, NotifyOnSourceUpdated=True,
IsAsync=True}"
SelectedIndex="0" IsEditable="False"
SelectionChanged="CbCombinationSelectionChanged"
SourceUpdated="CbCombinationSourceUpdated"
TargetUpdated="CbCombinationTargetUpdated"></ComboBox>
</ToolBar>
</ToolBarTray>
In the UserControl's code I have breakpoints on CbCombinationSelectionChanged, CbCombinationSourceUpdated and CbCombinationTargetUpdated.
The CbCombinationSelectionChanged fires once when the form containing the user control is first loaded. It is indeed called a second time when the Combinations collection is cleared, as #asktomsk said.
The source updated and target updated are not triggered - CbCombinationSourceUpdated and CbCombinationTargetUpdated are not called.
As the combo box is inside a usercontrol and the Combinations collection is within the view model, and as the view model doesn't have access to the combo box, I have no opportunity to set the selected index of the combo unless the events fire.
:(
The problem is in your this.Combinations.Clear();
When you do it, it sets SelectedItem = null and SelectedIndex = -1
So you should set selection again. If you have an access to your ComboBox then just write this cbCombination.SelectedIndex = 0; after filling Combinations list.
Of course you can bind SelectedItem/SelectedIndex to some property in code behind too.
BTW
Also it is not required to call NotifyPropertyChanged(#"Combinations"); after filling the collection because Combinations already implements INotifyPropertyChanged.
Update
To detect that your ObservableCollection was changed, subscribe to CollectionChanged event in your UserControl code behind. Make sure that you subscribed before collection changed!
Combinations.CollectionChanged += (s, e) =>
{
if (cbCombination.Items.Count > 0)
cbCombination.SelectedIndex = 0;
};
Another suggestion
Approach above is works when you do not need a smarter logic than just select zero index in combobox.
But often you will need a more complex logic to select some item. In this case you may add new property to your model:
public Combination SelectedCombination
{
get{ return _selectedCombination; }
set
{
_selectedCombination = value;
NotifyPropertyChanged("SelectedCombination");
}
}
and bind this property to your combobox:
<ComboBox Name="cbCombination" ItemsSource="{Binding Combinations}"
SelectedIndex="0" SelectedItem={Bindings SelectedCombination} />
In this case you can select any item when filling the Combinations collection and it will be automatically selected in combobox:
var combinationsList = CombinationsManager.CombinationsFor(someParam);
this.Combinations.Clear();
foreach (var combination in combinationsList)
this.Combinations.Add(combination);
if (Combinations.Count > 0)
SelectedCombination = Combinations[0];

Change Silverlight DataForm:DataField label value at runtime

I'm having a dataform which is binded to a property in my view-model in a Silverlight application, I've created my entity classes with WCF RIA Services and every property has the attribute of DisplayName which is shown in the dataform datafield label. what I need to do is to add a ":" at the end of every label in the custom datafields that I create.
The reason I need this to happen is because I have a grid in my page which is binded to the list of current objects (e.g. Employees) and I don't want ":" at the end of the grid headers, but I also need ":" when I'm trying to edit or add a new employee.
This is what I've done so far, but it's not working.
public class CustomDataField : DataField
{
public CustomDataField()
{
}
public new object Label
{
get { return base.Label; }
set
{
base.Label = value;
if( value is string )
{
base.Label = (string)value + ":";
}
}
}
}
(1)
When you don't let the DataForm autogenerate the fields, you have more control over the fields and can set the labels manually:
<tkt:DataForm AutoGenerateFields="False" AutoEdit="True">
<tkt:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<tkt:DataField Label="SomeLabel:">
<TextBox Text="{Binding SomeProperty, Mode=TwoWay}" />
</tkt:DataField>
[...]
</StackPanel>
</DataTemplate>
</tkt:DataForm.EditTemplate>
</tkt:DataForm>
(2)
If you need the auto-generating functionality, but you also need more control over how fields are displayed, you could wrap the DataForm into your own custom control. You'll have to implement the auto-generation yourself to build your own EditTemplate, which you'd assign to the DataForm. This is the road that I took.
(3)
Another quick and dirty way would be to iterate through the visual tree after the DataForm has rendered to change the labels. That goes pretty straightforward with a little help from the toolkit:
// needs System.Windows.Controls.Toolkit.dll
using System.Linq;
using System.Windows.Controls.Primitives;
foreach (var field in myDataForm.GetVisualDescendents().OfType<DataField>())
{
field.Label = field.Label + ":";
}
(4)
Finally, I just saw that there is an AutoGeneratingField event on the DataForm that could work (untested):
myDataForm.AutoGeneratingField += (sender, e) => e.Field.Label = e.Field.Label + ":";

How to get Listbox selected index?

I have a Listbox filled with Buttons. When the buttons is clicked then a new Page shows. But i wanna have the selected index of the listbox when i click on the button. But the listboxitem is never selected. Is there a way to get the index where the button lies by clicking the button. Thanks for help.
Example code
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Click="ViewDetail_Click" Content="{Binding Path=Area}"></Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The data context of the button will contain the source data item corresponding to the list item. You've not shown what your ItemsSource is for the list, so I can't show exactly what code you'd need. But something like this:
ObservableCollection<MyDataItem> items = new ObservableCollection<MyDataItem>();
...
myList.ItemsSource = items;
...
private void ViewDetail_Click(object sender, RoutedEventArgs e)
{
Button b = (Button) sender;
MyDataItem detailItem = (MyDataItem) b.DataContext;
int itemIndex = items.IndexOf(detailItem);
}
Of course, if the reason you're asking for the index in the first place is because you want to get hold of the corresponding data item, you don't actually need the index at all - the item's already right there in the data context.
By the way, I wouldn't use a ListBox here. The main thing ListBox brings to the party is selection, but by putting buttons in as the items, you're defeating that - as you've already discovered, the button is handling the mouse input before the list box has a chance to, meaning that you can't actually select the items. At which point, why have a ListBox? A base ItemsControl probably makes more sense. (Wrapped in a ScrollViewer if you need scrolling.)

Silverlight Windows Phone 7: How to add content to a PivotControl?

I have a pivot control:
PivotItem sectionPivot = new PivotItem()
{
Header = sect.Name,
Content = new StackPanel()
};
How can I add content to it?
Edit: The reason I'm doing it programmatically is because I don't know how many pivots there will be or what they will contain until runtime. Is there some way to do that in XAML?
If you're going to do it all programatically, just add stuff to the stack panel you just created.
var panel = new StackPanel();
panel.Children.Add(new TextBlock() { Text = "Hello" });
PivotItem sectionPivot = new PivotItem()
{
Header = sect.Name,
Content = panel;
};
I typed all that without doing any checking, but hypothetically that should work...
Another answer from me. The OP added info to the question that they don't know how many there could be, and if you could still do it in XAML.
Yes, you can.
The Pivot control has an ItemsSource property, and you could bind that to something in your class that is being populated dynamically.
<controls:Pivot Title="MY APPLICATION" ItemsSource="{Binding MyPivotItemsSource}" />
Each item in that source would end up as a pivotitem. You'd also have to set up templates and stuff, so its still a lot of work...

Resources