WPF Combobox is blank (data binding) - wpf

I followed a simple tutorial for comboboxes (http://www.wpf-tutorial.com/list-controls/combobox-control/).
Here is my XAML for the combobox :
<ComboBox Name="CoursesTeach" Grid.Row="7" Grid.Column="1" Width="150" Height="Auto" Margin="0,24">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Foreground="Black" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code behind :
public AddTrainer()
{
InitializeComponent();
using (Model1Container context = new Model1Container())
{
foreach (var row in context.CourseSet)
{
if (row.Discipline != null)
{
CoursesTeach.ItemsSource = row.Discipline;
}
MetroCustomBox.ShowOK(row.Discipline); // i can see right values
}
}
}
But the results are just NOT in the combobox, although I can perfectly can print them.
Thanks a lot for your responses.

To add items in your Combobox by code behind you may use Items property.
With your previous code :
foreach (var row in context.CourseSet)
{
if (row.Discipline != null)
{
CoursesTeach.Items.Add(row.Discipline);
}
}
But a better way, is use ItemsSource property, with binding, or set by a List.
With your previous code :
CoursesTeach.ItemsSource = context.CourseSet.Where(row => row.Dicipline != null).Select(row => row.Dicipline).ToList();

Related

WPF How to find a specific control in an ItemsControl with data binding

I have an ItemsControl which is bound to a list:
<ItemsControl x:Name="icFiles" ItemsSource="{Binding Path=files}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
<TextBlock x:Name="ThisTextBlock" Text="{Binding FileName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private readonly List<FileModel> files = new();
icFiles.ItemsSource = files;
I want to highlight certain text in the TextBlock in the ItemsControl. For this, I thought about using a TextPointer:
string? highlightText = "blue";
int highlightTextIndex = ThisTextBlock.Text.IndexOf(highlightText);
if(highlightTextIndex >= 0)
{
TextPointer textStartPointer = ThisTextBlock.ContentStart.DocumentStart.GetInsertionPosition(LogicalDirection.Forward);
TextRange? highlightTextRange = new TextRange(textStartPointer.GetPositionAtOffset(highlightTextIndex), textStartPointer.GetPositionAtOffset(highlightTextIndex + highlightText.Length));
highlightTextRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Blue);
}
}
How do I find this ThisTextBlock?
You need to access the item container's content template (which is the item's DataTemplate).
In case of the ItemsControl, you can use the following example to obtain a named element from the DataTemplate:
for (int itemIndex = 0; itemIndex < this.ItemsControl.Items.Count; itemIndex++)
{
var itemContainer = this.ItemsControl.ItemContainerGenerator.ContainerFromIndex(itemIndex) as ContentPresenter;
var textBlock = itemContainer.ContentTemplate.FindName("ThisTextBlock", itemContainer) as TextBlock;
HighlightText(textBlock);
}
A simple implementation that searches an element in the visual tree can be found at How to: Microsoft Docs: How to: Find DataTemplate-Generated Elements. You can copy and use the example's helper method FindVisualChild to search for elements by type rather than by name. The method is part of an example that shows how to get the content of the DataTemplate in case you use a ListBox or ListView.
In case you didn't modified the ListBoxItem template or don't expect it to change, you can use this simplified and faster version (to find named elements):
for (int itemIndex = 0; itemIndex < this.ListBox.Items.Count; itemIndex++)
{
var listBoxItemContainer = this.ListBox.ItemContainerGenerator.ContainerFromIndex(itemIndex) as ListBoxItem;
var templateRootBorder = VisualTreeHelper.GetChild(listBoxItemContainer, 0) as Border;
var contentHost = templateRootBorder.Child as ContentPresenter;
var textBlock = contentHost.ContentTemplate.FindName("TD", contentHost) as TextBlock;
}
Except for special use cases, it is highly recommended to use the ListBox instead of the ItemsControl. ListBox and ListView are both an extended ItemsControl. They both provide scrolling and a significantly improved performance.
First of all, you need to delete the Binding from code behind.
You can do this using Loaded event as follows:
<ItemsControl x:Name="icFiles" ItemsSource="{Binding Path=files}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
<TextBlock Loaded="ThisTextBlock_OnLoaded" x:Name="ThisTextBlock" Text="{Binding FileName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private void ThisTextBlock_OnLoaded(object sender, RoutedEventArgs e)
{
if (sender is TextBlock tb)
{
string? highlightText = "blue";
int highlightTextIndex = tb.Text.IndexOf(highlightText);
if (highlightTextIndex >= 0)
{
TextPointer textStartPointer = tb.ContentStart.DocumentStart.GetInsertionPosition(LogicalDirection.Forward);
TextRange? highlightTextRange = new TextRange(textStartPointer.GetPositionAtOffset(highlightTextIndex), textStartPointer.GetPositionAtOffset(highlightTextIndex + highlightText.Length));
highlightTextRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Blue);
}
}
}

how to apply different font styles in one string

I have a wpf app, c# that displays texts (teahcer's name) when a combobox is closed. Basically it is contained in 1 string and being passed to the classInstance.TeachersComboBoxText or Text="{Binding Path=TeachersComboBoxText}" . Everything is great but the client is requesting for the text to be in italic if the teacher is flagged as not default or secondary teacher. Basically, I need to have a string but different font styles is applied to it,depends on my data.
<ComboBox Name="instanceTeachersComboBox"
IsEditable="True"
IsReadOnly="True"
ItemsSource="{Binding Path=TeacherList}" DropDownClosed="teachersComboBox_DropDownClosed"
Text="{Binding Path=TeachersComboBoxText}"
FontWeight="{Binding Path=IsSelectedFontWeight}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
Background="{Binding Path=IsActiveColour}"
Content="{Binding Path=Display}"
Foreground="{Binding Path=IsClashColour}"
FontStyle="{Binding Path=EndDateFontStyle}"/>
<Image Source="/SchoolEdgeTimetable;component/Images/greentick16x16.png"
Margin="5,0,0,0"
Visibility="{Binding Path=IsSpecialitySubjectVisibility}"></Image>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
=== this is the code ====
if (classInstance.TeacherList.Any(c => c.IsSelected == true && c.IsDefault == false) || instanceCount != defaultCount)
{
System.Windows.Forms.RichTextBox rtf = new System.Windows.Forms.RichTextBox();
foreach (var item in data)
{
if (!item.IsDefault)
{
rtf.SelectionFont = new Font("Arial", 12, System.Drawing.FontStyle.Italic);
rtf.AppendText(item.Display);
}
text = GT.Join2Strings(text, item.IsDefault? item.Display : rtf.Text, "\n");
}
}
else
text = DEFAULT_TEXT;
classInstance.TeachersComboBoxText = text;
I tried rtf because it seems that I can't apply it in plain string, but the code is not working. Is there any way to solve this without using rtf? or if not why is my code not working? anyone ca suggest a better way to do this will be appreciated. thanks
UPDATE:
People are suggesting to modify the template which I already did, that is working great in showing the items when a combobox is open. One of my items is RED and in italic as you can see BUT What I am trying to do though is modify display of the combobox TEXT property. I have attached an image to get a better view on what I mean.
thanks
It's pretty simple if you know how to utilise <Run> tag of TextBlock control in code behind. See below code:
//loop through all the teachers
foreach (var teacher in vm.Teachers)
{
//check if teacher is default
if (teacher.IsDefault)
{
//add text to "tb" textblock, in italic style
tb.Inlines.Add(new Run(teacher.Name) { FontStyle = FontStyles.Italic });
}
else
{
//add text to "tb" textblock
tb.Inlines.Add(new Run(teacher.Name));
}
}
If you are keen to know more about inline formatting, here is a cool blog post for same. Let me know if you need any more help on this.
You could handle the Loaded event for the CheckBox in the ItemTemplate and set its Content property to a TextBlock of Inline elements, e.g.:
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
Background="{Binding Path=IsActiveColour}"
Foreground="{Binding Path=IsClashColour}"
FontStyle="{Binding Path=EndDateFontStyle}"
Loaded="CheckBox_Loaded">
</CheckBox>
<Image Source="/SchoolEdgeTimetable;component/Images/greentick16x16.png"
Margin="5,0,0,0"
Visibility="{Binding Path=IsSpecialitySubjectVisibility}"></Image>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
private void CheckBox_Loaded(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
dynamic dataObject = checkBox.DataContext;
string display = dataObject.Display;
TextBlock textBlock = new TextBlock();
//split the display string and add Inlines to the TextBlock as suggested by #Naresh Ravlani here...
checkBox.Content = textBlock;
}
Add a datatrigger that changes your font to Italic:
<DataTrigger Binding="{Binding IsDefault}" Value="1">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>

web.config Binding - An ItemsControl is inconsistent with its items source

i'm building a tool to display and edit the web.config and want to add an "ADD" Button so that i can add an additional connectionstring to the web.config.
ViewModel:
private ConnectionStringSettingsCollection _ConnectionStrings;
public ConnectionStringSettingsCollection ConnectionStrings
{
get
{
return _ConnectionStrings;
}
set
{
if (_ConnectionStrings != value)
{
_ConnectionStrings = value;
RaisePropertyChanged("ConnectionStrings");
}
}
}
private void ExecuteAddConnectionString()
{
ConnectionStrings.Add(new ConnectionStringSettings("a","b","c"));
}
private void ReadConfig()
{
ConnectionStrings = config.ConnectionStrings.ConnectionStrings;
}
public void CreateConfig()
{
string webDirPath = (PathWithoutFile());
var vdm = new VirtualDirectoryMapping(webDirPath, true, "web.config");
var wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", vdm);
config = WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
}
View:
<TabItem Header="ConnectionString">
<GroupBox Header="ConnectionStrings" BorderBrush="Black" BorderThickness="2" Margin="5">
<ListBox ItemsSource="{Binding ConnectionStrings}" Margin="5" Background="Transparent" BorderThickness="0">
<ListBox.ItemTemplate >
<DataTemplate >
<ContentControl >
<vm:ConnectionStringAdd Width="400" Margin="5"/>
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</TabItem>
So when i click on the Add Button it gives me the following Error:
An ItemsControl is inconsistent with its items source
I know that this is because the GUI doesn't receive a notification about the change, but how do i solve it in my case? Can i somehow make the Connectionstrings to a observable collection?
Many thanks

Not getting updates as per Selected date in view contains Datagrid binded with Generic List?

I have used tab control for view my usercontrols..
in 1st usercontrol
I have used datagrid to diplay Record and for Binding I have used generic List.
When want to change this List as per selected date then that collection is changed in database and in viewmodel also as List's set propery get executes but in view when i selected new tab and then go back to prevois tab at that time List's get property executes & then i am able get view as per selected date.
My main view and which contain 1st usercontrol as 1st tab item is given below:
Xaml code for above view is given below:
<DataGrid
Background="Transparent"
CanUserAddRows="True"
CanUserReorderColumns="False"
ItemsSource="{Binding Model_Transactions_TransactionsDetails_Jama,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HeadersVisibility="Column">
</DataGrid>
<Grid DockPanel.Dock="Bottom" VerticalAlignment="Bottom" >
<Border BorderBrush="Black" BorderThickness="1" >
<Label HorizontalAlignment="Left" HorizontalContentAlignment="Right" Width="75" Content="{Binding SumOfWeightJama,UpdateSourceTrigger=PropertyChanged}" FontFamily="Segoe UI" FontWeight="Bold" FontSize="16" />
</Border>
</Grid>
<DataGrid
Background="Transparent"
CanUserAddRows="True"
CanUserReorderColumns="False"
ItemsSource="{Binding Model_Transactions_TransactionsDetails_Udhar,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HeadersVisibility="Column">
</DataGrid>
<Grid DockPanel.Dock="Bottom" VerticalAlignment="Bottom">
<Border BorderBrush="Black" BorderThickness="1">
<Label Width="75" HorizontalAlignment="Left" HorizontalContentAlignment="Right" Content="{Binding SumOfWeightUdhar,UpdateSourceTrigger=PropertyChanged}" FontFamily="Segoe UI" FontWeight="Bold" FontSize="16"/>
</Border>
</Grid>
And View Model for above View is given below:
private DateTime _FilterDate ;
public DateTime FilterDate
{
get
{
return _FilterDate;
}
set
{
_FilterDate = value;
OnPropertyChanged("FilterDate");
Model_Transactions_TransactionsDetails_Jama = (ViewModel.AllDataCollactions.AllTransactionsDetails.Where(s => s.TransactionDate.Equals(FilterDate) && s.IsJama).OrderBy(s => s.TransactionsID)).ToList();
Model_Transactions_TransactionsDetails_Udhar = (ViewModel.AllDataCollactions.AllTransactionsDetails.Where(s => s.TransactionDate.Equals(FilterDate) && !s.IsJama).OrderBy(s => s.TransactionsID)).ToList();
}
}
public List<Model_TransactionsDetails> Model_Transactions_TransactionsDetails_Jama
{
get
{
return model_Transactions_TransactionsDetails_Jama;
}
set
{
model_Transactions_TransactionsDetails_Jama = value;
OnPropertyChanged("Model_Transactions_TransactionsDetails_Jama");
}
}
public List<Model_TransactionsDetails> Model_Transactions_TransactionsDetails_Udhar
{
get
{
return model_Transactions_TransactionsDetails_Udhar;
}
set
{
model_Transactions_TransactionsDetails_Udhar = value;
OnPropertyChanged("Model_Transactions_TransactionsDetails_Udhar");
}
}
public ViewModel_MasterBook()
{
FilterDate = DateTime.Now.AddDays(-1).Date;
InsertCommand = new RelayCommand(AddExecute, CanAdd);
}
Can any one help me How can i get view as per selected date immediately..
actually it should work i cant see an error. but when i use some kind of Lists in my WPF projects i use observablecollection with clear, add, delete.
but first i would change the binding
ItemsSource="{Binding Model_Transactions_TransactionsDetails_Jama,Mode=OneWay}"
because Mode=TwoWay makes no sense, you never set the itemssource from your datagrid to the viewmodel.
second i would change to ObservableCollection
public ObservableCollection<Model_TransactionsDetails> Model_Transactions_TransactionsDetails_Jama
{
get; private set;
}
with private setter because just initialize once.
//ctor
this.Model_Transactions_TransactionsDetails_Jama = new ObservableCollection<Model_TransactionsDetails>();
and then in your FilterDate setter fill the collection
this.Model_Transactions_TransactionsDetails_Jama.Clear();
var newdata = (ViewModel.AllDataCollactions.AllTransactionsDetails.Where(s => s.TransactionDate.Equals(FilterDate) && s.IsJama).OrderBy(s => s.TransactionsID)).ToList();
this.Model_Transactions_TransactionsDetails_Jama.AddRange(newdata);//AddRange is simply an extension method i wrote, you can simply use foreach and .Add()

wpf binding when using datatemplate

ok currently i have this piece of code:
<TabItem Style="{DynamicResource MyStyle" x:Name="TabCustomers" Padding="0,1,4,1"
Header={Binding Path=customersHeader}/>
Now i want to add an icon there so I do (by removing the header above):
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Stretch="UniformToFill" Source="{StaticResource customers}"/>
<TextBlock x:Key="textblock" Margin="4,0,0,0"
Text="{Binding Path=customersHeader}"/>
</StackPanel>
</TabItem.Header>
So far it's ok.
I would like to generalize this using a datatemplate. I assume i have to do this in my resource dictionary:
<DataTemplate x:Key="TabItemCustomersTemplate" DataType="{x:Type TabItem}">
<StackPanel Orientation="Horizontal">
<Image Stretch="UniformToFill" Source="{StaticResource customers}"/>
<TextBlock x:Key="textblock" Margin="4,0,0,0"
Text="{Binding Path=customersHeader}"/>
</StackPanel>
</DataTemplate>
and change this in my tabitem declaration:
<TabItem ... HeaderTemplate="{StaticResource TabItemCustomersTemplate}".../>
So i run into the following issues and questions:
1) binding doesnt work, why?
2) how can i access textblock from c#?
3) how can i generalize this so i dont have to copy this over and over again for different tab items (or other controls for the matter) so that i can pass my own text and image source each time? For example you might use this to create an image button and if you have 20 buttons the code becomes messy.
Any ideas?
Thank you.
if you template the header in a
tabitem, you do not need to set the
data type of the template. the
header is a property of the tab
item, it is actually a property of
type object, you can put anything in
there.
try removing the DataType="{x:Type
TabItem}" and see if it works.
you should not need to access the
textblock from c#, you should make
do with the binding system. place a
custom object in your header. then
bind this object to your textblock
then adjust the object and it will
manipulate the textblock. getting at
an element is always hard if it is
contained in a data template. you
should not need to. if you find
yourself walking the visual tree to
find a visual element you are doing
things the hard way
you can generalise this by following
suggestion 2, using a custom object,
removing the x:Key of your data
template and setting its DataType to
be the type of your custom object.
then wherever your custom object
appears you will get it data
templated properly
Try this, This is working for me
<Window.Resources>
<!-- <BitmapImage x:Key="customers" UriSource="einstein.jpg"/>-->
<DataTemplate x:Key="TabItemCustomersTemplate">
<StackPanel Orientation="Horizontal">
<Image Stretch="UniformToFill" Source="{Binding Path=Customers}"/>
<TextBlock Margin="4,0,0,0" x:Name="txt" Text="{Binding Path=CustomersHeader}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl Name="mytabcontrol">
<TabItem x:Name="TabCustomers" Padding="0,1,4,1" Header="{Binding}" HeaderTemplate="{StaticResource TabItemCustomersTemplate}">
<Label Content="myContent" Background="Red"/>
</TabItem>
</TabControl>
</Grid>
in code behind
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var lst = new List<People>();
lst.Add(new People() { CustomersHeader = "My Customer" });
this.DataContext = lst;
}
}
public class People
{
public string CustomersHeader { get; set; }
public BitmapImage Customers { get; set; }
}
Further you can find your textblock in code behind using this
TabPanel tabPanel = GetVisualChild<TabPanel>(mytabcontrol);
if (tabPanel != null)
{
foreach (UIElement element in tabPanel.Children)
{
TabItem tabItem = element as TabItem;
var image = FindNameFromHeaderTemplate<TextBlock>(tabItem, "txt");
}
}
public static T FindNameFromHeaderTemplate<T>(TabItem tabItem, String name) where T : UIElement
{
if (tabItem == null)
{
throw new ArgumentNullException("container");
}
if (tabItem.HeaderTemplate == null)
{
return null;
}
ContentPresenter contentPresenter = GetVisualChild<ContentPresenter>(tabItem);
if (contentPresenter == null)
{
return null;
}
T element = tabItem.HeaderTemplate.FindName(name, contentPresenter) as T;
return element;
}
public static T GetVisualChild<T>(Visual referenceVisual) where T : Visual
{
Visual child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++)
{
child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual;
if (child != null && child.GetType() == typeof(T))
{
break;
}
else if (child != null)
{
child = GetVisualChild<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}

Resources