I need to put all colors from class Colors to combobox, but without Transparent. I know how it made, but it is additionally condition - I have to do all using binding.
I have:
<Window.Resources>
<ObjectDataProvider ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="colorPropertiesOdp" />
</Window.Resources>
<ComboBox ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" DisplayMemberPath="Name" SelectedValuePath="Name"/>
and it provide all colors. But I don't know how I can delete Transparent.
Thanks for help!
I can't think of a pure XAML solution to this problem. Even a CollectionViewSource with a filter will require a function in either the codebehind or the viewmodel depending on your approach. So, you can save some code on both ends and just filter the list on the backend before its attached to the combobox. For the sake of simplicity the code below uses the window's codebehind instead of a viewmodel.
On the backend:
public static IEnumerable<String> ColorsWithoutTransparent
{
get
{
var colors = typeof (Colors);
return colors.GetProperties().Select(x => x.Name).Where(x => !x.Equals("Transparent"));
}
}
Modified XAML (Take note of the added Window DataContext):
<Window x:Class="WpfApplication1.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">
<Grid>
<ComboBox Margin="50" ItemsSource="{Binding ColorsWithoutTransparent}"/>
</Grid>
You can assign this to a CollectionViewSource and Filter the transparent.
<Window.Resources>
<ObjectDataProvider ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="colorPropertiesOdp" />
<CollectionViewSource x:Key="FilterCollectionView" Filter="CollectionViewSource_Filter" Source="{StaticResource colorPropertiesOdp}" />
</Window.Resources>
<ComboBox ItemsSource="{Binding Source={StaticResource FilterCollectionView}}" DisplayMemberPath="Name" SelectedValuePath="Name"/>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
System.Reflection.PropertyInfo pi = (System.Reflection.PropertyInfo)e.Item;
if (pi.Name == "Transparent")
{
e.Accepted = false;
}
else
{
e.Accepted = true;
}
}
}
Related
I'm trying to wrap my head around ReactiveUI. Most of it makes some degree of sense if you don't look at it too closely, but when I try to set up a TabControl everything explodes in my face.
I have a TabControl on my Window. I want to be able to add multiple different types of tabs to it dynamically at runtime based on different user actions. This answer explains a standard WPF way to do that, and it almost works, but since it's not ReactiveUI, anytime I try to open a tab with a Reactive view on it, everything blows up because the ViewModel dependency property hasn't been bound.
XAML:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:MyTabViewModel}">
<views:MyTabEditor/>
</DataTemplate>
</Window.Resources>
...
<TabControl Name="Multitab" Grid.Column="2" ItemsSource="{Binding Tabs}">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
ViewModel:
public ObservableCollection<ITabPage> Tabs { get; } = new();
public void AddNewTab()
{
var vm = new MyTabEditorViewModel();
Tabs.Add(vm);
}
XAML.cs
private void NewTab_Executed(object sender, ExecutedRoutedEventArgs e)
{
ViewModel.AddNewJob();
Multitab.SelectedIndex = Multitab.Items.Count - 1;
var last = Multitab.SelectedIndex;
this.OneWayBind(ViewModel, vm => vm.Tabs[last], v => GAH WHAT GOES HERE?!?);
}
And this is the part where I get lost. How do I set up the bindings to bind the new VM to the view that gets created for it? Multitab.SelectedItem returns the VM, not the View, and I can't seem to find any way to obtain the newly-created View object in order to bind it.
Does anyone know how to set this up properly?
Please refer to the below sample code.
Window1.xaml:
<reactiveui:ReactiveWindow x:Class="WpfApp1.Window1"
x:TypeArguments="local:ViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
xmlns:reactiveui="http://reactiveui.net"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<Grid>
<TabControl Name="Multitab">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<reactiveui:ViewModelViewHost ViewModel="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</reactiveui:ReactiveWindow>
Window.xaml.cs:
public partial class Window1 : ReactiveWindow<ViewModel>
{
public Window1()
{
InitializeComponent();
ViewModel = new ViewModel();
this.WhenActivated(disposableRegistration =>
{
this.OneWayBind(ViewModel,
viewModel => viewModel.Tabs,
view => view.Multitab.ItemsSource)
.DisposeWith(disposableRegistration);
});
}
}
View Model:
public class ViewModel
{
public ObservableCollection<ITabPage> Tabs { get; } =
new ObservableCollection<ITabPage>() { new MyTabEditorViewModel() };
}
Tab View Model:
public interface ITabPage { }
public class MyTabEditorViewModel : ITabPage
{
public string Name { get; } = "Name...";
}
TabView.xaml:
<reactiveui:ReactiveUserControl x:Class="WpfApp1.TabEditorView"
x:TypeArguments="local:MyTabEditorViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:reactiveui="http://reactiveui.net"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock>Tab content...</TextBlock>
</Grid>
</reactiveui:ReactiveUserControl>
TabView.xaml.cs:
public partial class TabEditorView : ReactiveUserControl<MyTabEditorViewModel>
{
public TabEditorView()
{
InitializeComponent();
}
}
I use one window to change the data, while using another window(MainWindow) to show the data.
Unexpectedly, when MainWindowViewModel catches the PropertyChanged event and RaisePropertyChanged to update MainWindow view, nothing happened in the view.
In the debugger, I found the MainWindowViewModel property has changed,and Debug has printed the message, but view not change.
I'm using Mvvmlight.
Sorry for my poor English.
I'd appreciate it if you could help me. XD!
Here is the View code:
<Window x:Class="OneTimetablePlus.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OneTimetablePlus.Views"
xmlns:tb="http://www.hardcodet.net/taskbar"
mc:Ignorable="d"
Title="MainWindow" Height="730" Width="91.52"
AllowsTransparency="True"
WindowStyle="None"
Background="Transparent"
ShowInTaskbar="False"
Topmost="True"
ResizeMode="NoResize"
DataContext="{Binding Main, Source={StaticResource Locator}}"
>
<ListBox ItemsSource="{Binding TodayDayCourses}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ShowName}" Style="{StaticResource LargeText}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Here is the ViewModel code:
public class MainViewModel : ViewModelBase
{
public MainViewModel(IDataProvider dataProvider)
{
DataProvider = dataProvider;
dataProvider.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == GetPropertyName(() => dataProvider.TodayDayCourse))
{
Debug.Print("Catch PropertyChanged TodayDayCourse");
RaisePropertyChanged(() => TodayDayCourses);
}
};
}
public List<Course> TodayDayCourses => DataProvider.TodayDayCourse2;
public IDataProvider DataProvider { get; }
}
As Clemens said, we should use => DataProvider.TodayDayCourse2.ToList() instead of => DataProvider.TodayDayCourse2.
Because the latter always returns the same instance, while is not a property changed.
I'm trying to make my fist UserControl in C#. It is a TabControll with some quality of life improvements. The goal is to be able to use it in various projects, so it has to be as generic as possible.
So far I have exposed the ItemSource through a DependencyProperty. But I'm suck with how to do the same with the ContentTemplate Property.
Here's an example of my code so far:
XAML:
<UserControl ...>
<UserControl.Resources>
<!-- some styles and templates -->
</UserControl.Resources>
<TabControl ItemsSource="{Binding ItemsSource}" SelectedIndex="{Binding selectedIndex}"
Style="{StaticResource FixatedTabControl}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}"/>
</UserControl>
The code behind:
public partial class DynamicTabControl : UserControl, INotifyPropertyChanged
{
public DynamicTabControl()
{
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ITabItem>), typeof(DynamicTabControl));
public IEnumerable<ITabItem> ItemsSource
{
get { return (IEnumerable<ITabItem>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
}
I can use the DynamicTabControl like so:
<Window x:Class="Demo.MainWindow"
...
xmlns:local="clr-namespace:Demo"
xmlns:foo="clr-namespace:DynamicTabUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
</foo:DynamicTabControl>
</Grid>
</Window>
But how can I make it possible to alter/add the contenTemplate of the UserControl's TabControl?
I would like to get it to behave like such:
<Window x:Class="Demo.MainWindow"
...
xmlns:local="clr-namespace:Demo"
xmlns:foo="clr-namespace:DynamicTabUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
<foo:DynamicTabControl.TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding someData}"/>
</DataTemplate>
</foo:DynamicTabControl.TabControl.ContentTemplate>
</foo:DynamicTabControl>
</Grid>
</Window>
I'm still learning, so please help me out.
Thank you in advance.
Add another dependency property to the UserControl:
public partial class DynamicTabControl : UserControl, INotifyPropertyChanged
{
public DynamicTabControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ITabItem>), typeof(DynamicTabControl));
public IEnumerable<ITabItem> ItemsSource
{
get { return (IEnumerable<ITabItem>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty TabContentTemplateProperty =
DependencyProperty.Register("TabContentTemplate", typeof(DataTemplate), typeof(DynamicTabControl));
public DataTemplate TabContentTemplate
{
get { return (DataTemplate)GetValue(TabContentTemplateProperty); }
set { SetValue(TabContentTemplateProperty, value); }
}
}
...and bind to it:
<UserControl>
<UserControl.Resources>
<!-- some styles and templates -->
</UserControl.Resources>
<TabControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
Style="{StaticResource FixatedTabControl}"
ContentTemplate="{Binding TabContentTemplate, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
You can then set this property in the XAML markup of the window:
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
<foo:DynamicTabControl.TabContentTemplate>
<DataTemplate>
<TextBox Text="{Binding someData}"/>
</DataTemplate>
</foo:DynamicTabControl.TabContentTemplate>
</foo:DynamicTabControl>
Using Xceed DataGrid for WPF
How can you use generated sample data source (generated in Expression Blend) as the source for DataGridCollectionViewSource? Is it possible?
<xcdg:DataGridCollectionViewSource x:Key="cvsSample"
Source="{Binding Source={x:Static Application.Current},Path=SampleDataSource}"/>
Doing this throw an error:
A value of type 'DataGridCollectionViewSource' cannot be added to a collection or dictionary of type 'UIElementCollection'.
I can set it directly in the DataGridControl like so:
<xcdg:DataGridControl ItemTemplate="{DynamicResource ItemTemplate}"
ItemsSource="{Binding Collection, Source={StaticResource SampleDataSource}}"
UpdateSourceTrigger="CellContentChanged"
Margin="10">
</xcdg:DataGridControl>
But I want to use the DataGridCollectionViewSource as it allows you to use the filtering, grouping etc. functionality.
Try this:
XAML:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<xcdg:DataGridCollectionViewSource x:Key="cvsSample" Source="{Binding}" />
</Window.Resources>
<Grid>
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource cvsSample}}"/>
</Grid>
</Window>
CS:
using Xceed.Wpf.Samples.SampleData;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = SampleDataProvider.GetProducts();
}
}
Look at jstreet's answer, but if that doesnt work for you, you can try doing what I did.
In Visual Studio go to Project > Add Reference > Extensions and add Xceed.Wpf.DataGrid.Samples.SampleData (remember to check the little box next to it).
App.xaml.cs
public partial class App : System.Windows.Application
{
protected override void OnStartup(StartupEventArgs e)
{
Xceed.Wpf.DataGrid.Licenser.LicenseKey = "XXXXX-XXXXX-XXXXX-XXXX";
DataSet musicDataSet = Xceed.Wpf.DataGrid.Samples.SampleData.DataProvider.GetMusicLibraryDataSet();
m_songs = musicDataSet.Tables["Songs"];
base.OnStartup(e);
}
private DataTable m_songs;
public DataTable Songs
{
get
{
return m_songs;
}
}
}
MainWindow.xaml
<Window.Resources>
<xcdg:DataGridCollectionViewSource x:Key="cvsSongs"
Source="{Binding Source={x:Static Application.Current},Path=Songs}">
</xcdg:DataGridCollectionViewSource>
</Window.Resources>
<Grid>
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource cvsSongs}}"/>
</Grid>
Can't believe I struggled this much just to have missed a reference...
I'm working on a WinRT application where i have a Listview with a ComboBox.
The Listview has a particular ObservableCollection as Itemssource, The ComboBox Should have another ObservableCollection as ItemsSource because i should be able to dynamicaly change the contents of the ComboBox.
I'm using the MVVM-Light framework, The ObservableCollections are filled in the ViewModel and displayed through databinding.
I'll give you an example Xaml code:
<Page x:Class="MvvmLight2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Page.Resources>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding CollectionOne}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding StringOne}"></TextBlock>
<ComboBox ItemsSource="{Binding CollectionTwo}" Width="500">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringTwo}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And Corresponding ViewModel:
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
CollectionOne = new ObservableCollection<ClassOne>();
for (int i = 0; i < 4; i++)
{
var temp = new ClassOne()
{
StringOne = "String " + i.ToString()
};
CollectionOne.Add(temp);
}
CollectionTwo = new ObservableCollection<ClassTwo>();
CollectionTwo.Add(new ClassTwo("ADV"));
CollectionTwo.Add(new ClassTwo("Wettelijk"));
}
private ObservableCollection<ClassOne> _collectionOne;
public ObservableCollection<ClassOne> CollectionOne
{
get { return _collectionOne; }
set
{
if (_collectionOne == value)
{
return;
}
_collectionOne = value;
RaisePropertyChanged(() => CollectionOne);
}
}
private ObservableCollection<ClassTwo> _collectionTwo;
public ObservableCollection<ClassTwo> CollectionTwo
{
get { return _collectionTwo; }
set
{
if (_collectionTwo == value)
{
return;
}
_collectionTwo = value;
RaisePropertyChanged(() => CollectionTwo);
}
}
}
In ClassOne and ClassTwo are for the example just one property in each class with a string.
Both collections have to remain seperate because they can be different in length when randomly filled.
EDIT
#Josh I followed your instructions but it still doesn't seem to work, Here are my adjustments:
<Page x:Class="MvvmLight2.MainPage"
x:Name="MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Page.Resources>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding CollectionOne}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding StringOne}"></TextBlock>
<ComboBox ItemsSource="{Binding ElementName=MyControl, Path=CollectionTwo}" Width="500">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringTwo}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Since you are using the ViewModel Locator to set your datacontext you can reuse this to find the property CollectionTwo.
Your binding would look like this:
<ComboBox ItemsSource="{Binding Path=Main.CollectionTwo, Source={StaticResource Locator}}" />
You need to move up one level in the datacontext to search the view model instead of the item that is bound at the list view level using RelativeSource:
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Page}}, Path=CollectionTwo}" />
and for WinRT situations, use a control name:
ElementName=MyControl
instead of searching by AncestorType and give the page a name of 'MyControl'. It would then look like
<ComboBox ItemsSource="{Binding ElementName=MyControl, Path=DataContext.CollectionTwo}" />
and your Page would look like
<Page x:Name="MyControl"
The ComboBox binding is relative to the Binding of the ListItem. So it searches for CollectionTwo as a property of ClassOne. Either look at the RelativeSource to bind to or move the CollectionTwo to class ClassOne. That way, you can easily build up different lists for each ListViewItem.