I have a simple class:
public class Foo
{
public string Text { get; set; }
public bool AppleStyle { get; set; }
public Foo(string text, bool applyStyle)
{
Text = text;
ApplyStyle = applyStyle;
}
public override string ToString()
{
return Text;
}
}
Which is then used to add items to a ListBox:
var one = new Foo("Some Text", false);
var two = new Foo("More Text", true);
MyListBox.Items.Add(one);
MyListBox.Items.Add(two);
I then loop through the items in the ListBox to figure out how to style them. This is where I get stuck. I tried inheriting from ListBoxItem for the class, but no items get added if I do that.
for (int i = 0; i < MyListBox.Items.Count; i++)
{
if(((Foo)MyListBox.Items[i]).ApplyStyle)
{
((ListBoxItem)MyListBox.Items[i]).Style = Resources["MyStyle"] as Style;
}
}
Update:
In MainWindow.xaml:
<Window.Resources>
<Style x:Key="MyStyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="Bisque"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Style>
</Window.Resources>
Update 3:
Making some progress, just need to know how to refresh the styles (after clicking on a button). Plus if Resource is not in MainWindow.xaml, would it then look in App.xaml before returning null?
MainWindow.xaml
<Window...>
<Window.Resources>
<Style x:Key="MyClass" TargetType="ListBoxItem">
<Setter Property="Background" Value="Bisque"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Style>
<myapp:MyListItemStyleSelector x:Key="MyListItemStyleSelector" />
</Window.Resources>
<Grid>
...
<ListBox .... ItemContainerStyleSelector="{StaticResource: MyListItemStyleSelector}" />
...
</Grid>
</Window>
MyListItemStyleSelector.cs
public class MyListItemStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
int index = ic.ItemContainerGenerator.IndexFromContainer(container);
Style applyStyle = null;
var data = item as Foo;
if (data != null && data.ApplyStyle)
{
applyStyle = ic.TryFindResource("MyStyle") as Style;
}
return applyStyle;
}
}
I think you have some sort of mixup here, i try to explain as good as i can.
First of all You usually never need to change the Style in code, like your last code block.
One thing that is difficult to understand in the beginning is the use of a ItemContainerStyle and DataTemplate.
I would suggest that you do the following.
Instead of changing the style off your ListBoxItem see if it is sufficient to use a DataTemplate. The DataTemplate defines how the Content of your ListBoxItem is shown.
<DataTemplate TargetType="{x:Type Foo}">
<!-- your visuals and controls here -->
</DataTemplate>
Now if you want to use different datatemplates you could use different classes and create different DataTemplates for them, or you use a DataTemplateSelector
public class FooTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
var mdl = item as Foo;
if( mdl.AppleStyle )
return element.FindResource("appleTemplate") as DataTemplate;
return element.FindResource("normalTemplate") as DataTemplate;
}
}
Create that templateselector in xaml and reference it in your listbox
<myNs:FooTemplateSelector x:Key="fooTemplateSelector"/>
<Listbox DataTemplateSelector="{StaticResource fooTemplateSelector}"/>
now you need to create 2 DataTemplates appleTemplate *normalTemplate* and you can easyl distinguish which data template to use vial the selector. Which is done automatically in the ListBox for you.
If you really want to change the Style of the ItemContainer you can use ItemContainerStyleSelector which works similar to the DataTemplateSelector. But i would not suggest it. You should supply the content and leave the ListBoxItem as it is, only if you want to modify the design(in this case, the selection color etc.), otherwise it might confuse the user or break functionality.
If you add data-objects directly to the ListBox the container-items will be generated automatically, you cannot get them this way.
Use the ItemContainerGenerator:
((ListBoxItem)MyListBox.ItemContainerGenerator.ContainerFromIndex(i)).Style = Resources["MyStyle"] as Style;
Why not do this in the XAML?
<ListBox Name="MyListBox">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding ApplyStyle}" Value="True">
<Setter Property="Background" Value="Bisque" />
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
</ListBox>
But your overall problem is that ListBox.Items returns a collection of data objects, not XAML Controls. To get the XAML control that contains the Data Object you have to do as H.B. suggested and use MyListBox.ItemContainerGenerator.ContainerFromItem(dataObject) to get the XAML Container for the data object. Just be sure you wait until after the ItemContainerGenerator has finished rendering items to get the container (I believe it has a Status property or StatusChanged event you can use... it's been a while and I can't remember the exact syntax)
Related
In listbox binding the values from ItemsSource. If doubleclick any item from listbox, it will clear the itemsSource value it will add the combobox control to the specified listboxselected index. How to achieve this?
Now it shows like "Element already has a logical parent. It must be detached from the old parent before it is attached to a new one"
//Get the index value of selected Item
var index = lstbxindex.Items.IndexOf(lstbxindex.SelectedItem);
m_combobox.Visibility = Visibility.Visible;
projectInformationList = null;
lstbxindex.ItemsSource = null;
lstbxindex.Items.Clear();
lstbxindex.Items.Insert(index, m_combobox); //InvalidOperationException thrown
m_combobox.Focus();
To Remove the Items from list I used this code,
IEditableCollectionView items = lstbxindex.Items;
if (items.CanRemove)
{
items.Remove(lstbxindex.SelectedItem);
}
I don't know how to add the control in listbox selectedindex while itemsSource is in use.
I tried ->
listbox.Items.Add(combox); it shows, can't able to add items while itemsSource is in use but in this code I didn't mention the selected index. But I want to add a control for listbox selectedindex position while double click any items from listbox.
Edit
I tried to add the items without using itemsSource like,
foreach (DTorow rowdata in table.Rows)
{
lstbox.Items.Add(rowdata .Name);
}
But it did not show the values in listbox. If it is shows the value in listbox simply add a combobox into specified index using this code listbox.Items.Insert(0"combobox) it will not shows invalidException(itemsSource is in use can't able to add new item).
Here is a simple example of how to do it with the standard WPF approach, i.e. MVVM and data templating. As already said in a comment to your previous question, you'll have to read the Data Binding Overview and Data Templating Overview articles to understand it, but this is unavoidable if you want to learn WPF.
The view model and the MainWindow code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.Items.Add(new DataItem { Text = "Item 1" });
vm.Items.Add(new DataItem { Text = "Item 2" });
vm.Items.Add(new DataItem { Text = "Item 3" });
vm.Items.Add(new DataItem { Text = "Item 4" });
DataContext = vm;
}
}
public class DataItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string text;
public string Text
{
get { return text; }
set
{
text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
public class ViewModel
{
public ObservableCollection<DataItem> Items { get; }
= new ObservableCollection<DataItem>();
}
And the XAML with two different ControlTemplates of a ListBoxItem, depending on
whether it's selected or not:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<TextBlock Margin="5,3" Text="{Binding Text}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<TextBox Text="{Binding Text}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I'm working on a UWP app. I want to iterate through all the ListViewItems of a ListView in a page. Here's the xaml for the ListView.
<ListView x:Name="DownloadTaskListView"
ItemsSource="{x:Bind ViewModel.CompletedDownloads}"
HorizontalContentAlignment="Stretch"
Background="{x:Null}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:DownloadTask">
<Grid x:Name="ItemViewGrid" Background="{x:Null}" Margin="4,0,0,0">
....
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
I use this piece of code to achieve this.
foreach(ListViewItem item in DownloadTaskListView.Items)
{
// Do something useful
}
But it gave me an Exception. Because I set the DataType of DataTemplate so runtime throw me an exception that it cannot convert from from DownloadTask (In this case the data type) to the ListViewItem. So I want to ask what is the other way of accessing ListViewItems?
You can use ItemsControl.ContainerFromItem method to find container corresponding to the specified item, then get the root element of this container, in your case it's a Grid. For example like this:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
foreach (var item in DownloadTaskListView.Items)
{
var listviewitem = item as DownloadTask;
var container = DownloadTaskListView.ContainerFromItem(listviewitem) as ListViewItem;
var ItemViewGrid = container.ContentTemplateRoot as Grid;
//TODO:
}
}
Just be aware that if you want to use this method in SelectionChanged event of your listview, you can just pass the selected Item into ContainerFromItem method, otherwise it will not find the ListBoxItem.
I should say, if it is possible, using data binding is better.
Since you are setting ItemsSource as ViewModel.CompletedDownloads do the Item loop on the same.
foreach(var Items in ViewModel.CompletedDownloads)
{
//Do Something Useful.
}
Simple syntax question. Programming silverlight 4 on VS2010. I created a button style in xaml:
<UserControl.Resources>
<Style x:Key ="TestbuttonStyle" TargetType="Button">
<Setter Property="Width" Value="150"></Setter>
<Setter Property="Margin" Value="0,0,0,10"></Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Source="http://i40.tinypic.com/j5k1kw.jpg" Height="20" Width="20" Margin="-30,0,0,0"></Image>
<TextBlock Text="sampleuser
sample number" Margin="5,0,0,0"></TextBlock>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
I need to create a button in the code behind, but using this style. I tried doign something like this:
Button btn = new Button();
//btn.Style = {TestbuttonStyle}; -what do i put here?
grid.children.add(btn);
how to I apply the style and add to my usercontrol grid?
Initially I thought you were working with WPF. Then I realized it's about Silverlight which doesn't have a hierarchical resource look-up helper method similar to WPF's FindResource or TryFindResource, respectively.
However, a quick search on the internet gave up this article which describes a nice extension method you can use:
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
var currentElement = element;
while (currentElement != null)
{
var resource = currentElement.Resources[resourceKey];
if (resource != null)
{
return resource;
}
currentElement = currentElement.Parent as FrameworkElement;
}
return Application.Current.Resources[resourceKey];
}
Then you can use it like this:
btn.Style = (Style)this.TryFindResource("TestbuttonStyle");
I am wondering if anyone could explain me the difference between
binding a selected value of a Collection to a comboBox.
Or Binding the value to a Button Content.
Like that
<ComboBox x:Name="_culturedTitleViewModelSelector" Visibility="Hidden" Style="{StaticResource ResourceKey=_culturedTitleViewModelSelectorStyle}"
ItemsSource="{Binding Path=AvailableCultures, Source={x:Static Localized:ResourcesManager.Current}}"
SelectedValue="{Binding Path=CurrentCulture, Source={x:Static Localized:ResourcesManager.Current}}"
<Button x:Name="LanguageBtn" Content="{Binding Path=CurrentCulture, Source={x:StaticLocalized:ResourcesManager.Current}}"
The issue is If i Don't use the ComboBox up there, the DependencyProperty I Have in another class is not being called.
But if I Use the comboBox everything works...
Altought the comboBox doesnt do anything it's just a "workarround"
In my CS code when i CLick on my button I DO that :
ResourcesManager.Current.SwitchToNextCulture();
//We use a dummy comboBox to make sure the LanguageBehavior Property is being notified.
_culturedTitleViewModelSelector.SelectedItem = ResourcesManager.Current.CurrentCulture;
And if I Dont set the SelectedItem of the combobox to another culture. My languageBehavior class is not notified.
:
public class LanguageBehavior
{
public static DependencyProperty LanguageProperty =
DependencyProperty.RegisterAttached("Language",
typeof(string),
typeof(LanguageBehavior),
new UIPropertyMetadata(OnLanguageChanged));
public static void SetLanguage(FrameworkElement target, string value)
{
target.SetValue(LanguageProperty, value);
}
public static string GetLanguage(FrameworkElement target)
{
return (string)target.GetValue(LanguageProperty);
}
private static void OnLanguageChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var element = target as FrameworkElement;
if (e.NewValue!=null)
element.Language = XmlLanguage.GetLanguage(e.NewValue.ToString());
}
}
I'd expect ComboBox Content to work the same as Button Content.
In my Generic.Xaml i do that :
<Style TargetType="{x:Type TextBlock}" x:Key="_textBlockLanguageProperty">
<Setter Property="WpfServices:LanguageBehavior.Language" Value="{Binding Path=CurrentCulture, Source={x:Static Localized:ResourcesManager.Current}}"
/>
</Style>
And that is CurrentCulture
public CultureInfo CurrentCulture
{
get { return CultureProvider.Current; }
set
{
if (value != CultureProvider.Current)
{
CultureProvider.Current = value;
OnCultureChanged();
}
}
}
Current :
public static ResourcesManager Current
{
get
{
if (_resourcesManager == null)
{
var cultureProvider = new BaseCultureProvider();
_resourcesManager = new ResourcesManager(cultureProvider);
_resourcesManager.Init();
}
return _resourcesManager;
}
}
EDIT :
My _culturedTitelViewModelSelectorStyle is
<Style TargetType="{x:Type ComboBox}" x:Key="_culturedTitleViewModelSelectorStyle">
<Setter Property="DisplayMemberPath" Value="DisplayName" />
<Setter Property="SelectedValuePath" Value="." />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="MaxHeight" Value="40" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Margin" Value="5" />
<Setter Property="SelectedIndex" Value="0" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
In the ComboBox you are binding the SelectedValue to a specific culture. This will select that culture from the list of available cultures, and therefor, trigger a set on the CurrentCulture property.
The Content property of a Button is merely displaying something to the user, it is not doing any assigning. It reads the property value and then displays it. That is why you need to manually change the Culture in the Click event to get it to do anything.
If you want the user to be able to select a value from a list of available values, a ComboBox or ListBox is the way to go. A Button is for triggering a specific action, not for selecting from a list.
I read the example of using MultiselectList in WindowsPhoneGeek
part1 part2
When I implementing the SelectAll and UnSelectAll function of the MultiselectList, I find that not all the items in MultiselectList were Select/UnSelect properly if the number of items over a certain limit. Some of the items won't go into the if section in following code
DependencyObject visualItem = itemContainerGenerator.ContainerFromItem(pizzaOption);
MultiselectItem multiselectItem = visualItem as MultiselectItem;
if (multiselectItem != null)
{
// NOTE: this will also add an item to the SelectedItems collection
multiselectItem.IsSelected = selected;
}
I guess it's because not all the multiselectItem were shown in UI that time.
But I do need the SelectAll to select all the items in MultiselectList not only the items in UI. WHAT CAN I DO? I've been thinking about binding the IsSelected Property to viewmodel as a workaround, but I tried the following code and got exception when parsing the xaml of that page.
<toolkit:MultiselectList.ItemContainerStyle>
<Style TargetType="toolkit:MultiselectItem">
<Setter Property="HintPanelHeight" Value="75"/>
<Setter Property="IsSelected" Value="{Binding IsMarked, Mode=TwoWay}"/>
</Style>
</toolkit:MultiselectList.ItemContainerStyle>
Please help me..
Thank you in advance :D
You get a xaml parsing error because your
<Style TargetType="toolkit:MultiselectItem">
<Setter Property="HintPanelHeight" Value="75"/>
<Setter Property="IsSelected" Value="{Binding IsMarked, Mode=TwoWay}"/>
</Style>
Should be on a resources, let's say
<toolkit:MultiselectList.Resources>
<Style TargetType="toolkit:MultiselectItem">
<Setter Property="HintPanelHeight" Value="75"/>
</Style>
</toolkit:MultiselectList.Resources>
For me, I would bind the datacontext of the MultiselectList to a class and do a foreach on its items like
DataContext = new List<ClassADataContext>();
foreach(var items in DataContext)
{
... logic
items.IsMarked = false; or whatever
}