This might have been answered before.
I am trying to edit my TextBlock dynamically from an object that has a string.
However. It doesn't seem to be updated from the View. But the value is updated. Just that it doesn't display it. Is there any way for that? Is there a repaint method?
What should I do?
Code:
ViewModel:
private String title = "";
public string Title { get { return title; }
set
{
title = value;
NotifyOfPropertyChange(() => Title);
}
}
View.XAML:
<TextBlock x:Name="Title"
Text="{Binding Path=Title, Mode=TwoWay}"
Grid.Row="2"
VerticalAlignment="Top"
FontSize="18"
Grid.ColumnSpan="3"
TextAlignment="Center"
FontWeight="Light"/>
It looks like the solution here is to use Caliburns EventAggregator to communicate with several VM. This was the solution I came up with and now it works :)
Related
Faced the need to select a fragment of text in TextBlock, namely certain keywords on which the ListBox was filtered, this text block itself and containing
XAML variant, title property is not bound
<ListBox Name="ProcedureList" ItemsSource="{Binding Path=ProceduresView.View}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="ProcedurePanel" PreviewMouseDown="ProcedurePanel_OnPreviewMouseDown">
<DockPanel Width="{c:Binding ElementName=MainPanel, Path=Width-40}">
<!--<TextBlock Name="MainText" TextWrapping="Wrap" FontSize="16" Text="{Binding Path=title}" HorizontalAlignment="Left" />-->
<htb:HighlightTextBlock Name="MainText" TextWrapping="Wrap" FontSize="16" Text="{Binding Path=title}" HorizontalAlignment="Left">
<htb:HighlightTextBlock.HighlightRules>
<htb:HighlightRule
IgnoreCase="{Binding IgnoreCase, Source={StaticResource SourceVm}}"
HightlightedText="{Binding Path=title, Converter={StaticResource getFilter}}">
<htb:HighlightRule.Highlights>
<htb:HighlightBackgroung Brush="Yellow"/>
</htb:HighlightRule.Highlights>
</htb:HighlightRule>
</htb:HighlightTextBlock.HighlightRules>
</htb:HighlightTextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
A component written by our compatriot with open source is used
Component
Description of component
The commented code is an old TexBlock with no selection
The new HighlightTextBlock component perfectly selects the text if you use a static resource, as in the example, but when I try to bind it to the current text it can not find this field :(, I'm new in WPF help figure it out
HightlightedText="{Binding Path=title, Converter={StaticResource getFilter}}"
How correctly to anchor this property to title?
DataContext structure
public ObservableCollection<Procedure> Procedures { set; get; }
public CollectionViewSource ProceduresView { set; get; } = new CollectionViewSource();
....
Procedures = new ObservableCollection<Procedure>();
ProceduresView.Filter += Procedures_Filter;
ProceduresView.Source = Procedures;
....
public class Procedure : ObservableObject
{
....
public String title { get; set; }
....
}
....
// Simple filtering
void Procedures_Filter(object sender, FilterEventArgs e)
{
Procedure procedure = (Procedure) e.Item;
Boolean flag = false;
if (!string.IsNullOrEmpty(filter))
{
Setting.Filter sfilter = new Setting.Filter();
sfilter.type = "искать везде";
sfilter.text = filter;
ObservableCollection<Setting.Filter> arr = new ObservableCollection<Setting.Filter>();
arr.Add(sfilter);
if (Utils.AssignedProcedureFromFilter(procedure, arr)) flag = true;
}
else flag = true;
e.Accepted = flag;
}
Video with problem description
Simplified project emitting my functional
On the Russian-speaking forum they explained to me that:
Your case, in fact, is more serious. DataContext you, apparently, the
right one. But your Binding expression is inside the HighlightRules
property setter, which is not part of the visual tree (because it is
not available as a Child element of your control). And elements that
are not inside the visual tree, participate in bindings are only
limited: they do not inherit DataContext, nor access by name through
ElementName. As a solution, bind to an element via x: Reference. In my
(heavily cut) test case, HightlightedText = "{Binding Path =
DataContext.title, Source = {x: Reference MainText}} is triggered."
But, if directly replaced by this, a strange error works: 'Can not
call MarkupExtension. ProvideValue because of a cyclic dependency. The
properties inside the MarkupExtension can not reference objects that
reference the MarkupExtension result.
The workaround for the error was found here: you need to put your element in resources. We get this:
XAML, modified according to the recommendations
<ListBox Name="ProcedureList" ItemsSource="{Binding Path=ProceduresView.View}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="ProcedurePanel" PreviewMouseDown="ProcedurePanel_OnPreviewMouseDown">
<DockPanel Width="{c:Binding ElementName=MainPanel, Path=Width-40}">
<!--<TextBlock Name="MainText" TextWrapping="Wrap" FontSize="16" Text="{Binding Path=title}" HorizontalAlignment="Left" />-->
<htb:HighlightTextBlock Name="MainText" TextWrapping="Wrap" FontSize="16"
Text="{Binding Path=title}" HorizontalAlignment="Left">
<htb:HighlightTextBlock.Resources>
<htb:HighlightRule x:Key="HR"
IgnoreCase="{Binding IgnoreCase, Source={StaticResource SourceVm}}"
HightlightedText="{Binding Path=DataContext.title, Source={x:Reference MainText}, Converter={StaticResource getFilter}}">
<htb:HighlightRule.Highlights>
<htb:HighlightBackgroung Brush="Yellow"/>
</htb:HighlightRule.Highlights>
</htb:HighlightRule>
</htb:HighlightTextBlock.Resources>
<htb:HighlightTextBlock.HighlightRules>
<htb:HighlightRulesCollection>
<StaticResource ResourceKey="HR"/>
</htb:HighlightRulesCollection>
</htb:HighlightTextBlock.HighlightRules>
</htb:HighlightTextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I was given advice on the restructuring of XAML, through resources, this partially solved the problem (I successfully got the title text in the converter), but the element ceased to perform its functions (allocation) During the discussion, it was suggested that the component itself should be finalized
#iRumba: In theory, the whole trick should not be necessary if you put
the HighlighRule collection (also) in a visual tree. Then the
DataContext will be automatically inherited and on idea the binding
through ElementName too will work.
#iRumba: I do not remember exactly. It seems, it is necessary to
specify to add all HighlightRule as LogicalChildren (for this purpose
on idea it is necessary to redefine protected internal override
IEnumerator LogicalChildren). This is a complicated, advanced
technique, yes.
Sorry for Google Translator
Found a solution
public class SearchHightlightTextBlock : TextBlock
{
public SearchHightlightTextBlock() : base() { }
public String SearchText
{
get { return (String)GetValue(SearchTextProperty); }
set { SetValue(SearchTextProperty, value); }
}
private static void OnDataChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
TextBlock tb = (TextBlock)source;
if (tb.Text.Length == 0)
return;
string textUpper = tb.Text.ToUpper();
String toFind = ((String)e.NewValue).ToUpper();
int firstIndex = textUpper.IndexOf(toFind);
String firstStr = "";
String foundStr = "";
if (firstIndex != -1)
{
firstStr = tb.Text.Substring(0, firstIndex);
foundStr = tb.Text.Substring(firstIndex, toFind.Length);
}
String endStr = tb.Text.Substring(firstIndex + toFind.Length,
tb.Text.Length - (firstIndex + toFind.Length));
tb.Inlines.Clear();
tb.FontSize = 16;
var run = new Run();
run.Text = firstStr;
tb.Inlines.Add(run);
run = new Run();
run.Background = Brushes.Yellow;
run.Text = foundStr;
tb.Inlines.Add(run);
run = new Run();
run.Text = endStr;
tb.Inlines.Add(run);
}
public static readonly DependencyProperty SearchTextProperty =
DependencyProperty.Register("SearchText",
typeof(String),
typeof(SearchHightlightTextBlock),
new FrameworkPropertyMetadata(null, OnDataChanged));
}
Use
<parser:SearchHightlightTextBlock SearchText="{Binding Path=title, Converter={StaticResource getFilter}}" Text="{Binding title}"/>
So I have a customised GridView with a data template that contains a TextBox and is populated by a list of a custom class called Player. I need to be able to retrieve both the instance of Player and the text in the TextBox and save them to a new custom class called Score.
<GridView x:Name="gridScore" ItemsSource="{x:Bind PlayerList}" IsItemClickEnabled="True">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Player">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="txtbxGridScore" TextChanged="txtbxGridScoreChangedEventHandler" />
<Image Source="{x:Bind ProfilePicture}"/>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{x:Bind FullName}" />
<TextBlock Text="{x:Bind Alias}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Button x:Name="buttonSave" Content="Save Scores" Style="{StaticResource BarButtonStyle}" Click="buttonSave_Click"/>
I come from a web-based Java background so this is a little bit new to me but it seems like it should be a fairly simple exercise.
Initially, I tried iterating through the GridView upon a Button Click and grabbing each Player item along with the TextBox Text and saving them to a List<> of Score, however, getting the TextBox value proved troublesome.
I then tried initialising a page scope List<> of Score and simply updating it each time the TextBox value was changed, however, I wasn't able to make this work either.
A solution for either approach will work fine for my purposes. Any input is appreciated!
If I correctly understood you this is one of the way to resolve your problem.
So let's assume that your model class Player have this structure:
public class Player {
public int PlayerID { get; set; }
public string ProfilePicture { get; set; }
public string FullName { get; set; }
public string Alias { get; set; }
public float PlayerScore { get; set; } // To store textbox value
}
So you can resolve this by using two way binding.
XAML part will look something like this:
<GridView x:Name="gridScore"
ItemsSource="{x:Bind PlayerList}"
IsItemClickEnabled="True">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Player">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="txtbxGridScore"
Text="{x:Bind PlayerScore, Mode=TwoWay}" />
<Image Source="{x:Bind ProfilePicture}" />
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{x:Bind FullName}" />
<TextBlock Text="{x:Bind Alias}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Button x:Name="buttonSave"
Content="Save Scores"
Click="buttonSave_Click" />
I have initialized your PlayerList with some dummy data like this:
PlayerList = new ObservableCollection<Player>() {
new Player() {
FullName = "Player A", Alias = "AAA"
},
new Player () {
FullName = "Player B", Alias = "BBB"
}
};
As you can see in XAML I am binding your text box with PlayerScore property of Player model.
When I run this App I get screen like this:
I will input some data into TextBox and click Save button:
When I click on Save it will trigger the event that you wrote in Button part
In that event I have one foreach loop that will iterate through the list and one breakpoint and as you can see on first item "Player A" the PlayerScore value is 10:
Now you can find your players with some ID property or with some other way that you want. This is the most simple way to accomplish what you want.
Remark: This could be solved in a better way using MVVM pattern and other stuff but as you mentioned you are beginner so maybe it is better for you to solve it like this and after that go with more advanced technique. Hope that this was helpful for you.
I'm working in huge application and I have one small problem
My Application has two languages (Arabic / English).
I have ComboBox And I would like to change the display content according to the language.
This is my ComboBox XAML:
<ComboBox x:Name="cmbCustomerGroup" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="5"
Margin="2" SelectedValuePath="CustomerGroupId" Validation.Error="Validation_Error"
SelectedValue="{Binding Path=CustomerGroupId, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}">
<!--<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>-->
</ComboBox>
This is my method:
private void FillCustomerGroups()
{
var oClsCustomers = new ClsCustomerGroups();
var lstCustGrps = oClsCustomers.GetData();
cmbCustomerGroup.ItemsSource = lstCustGrps.ToList<TbCustomerGroups>();
cmbCustomerGroup.DisplayMemberPath = Helper.CurrLang == Helper.SystemLanguage.Arabic ? "CustomerGroupAName" : "CustomerGroupEName";
cmbCustomerGroup.SelectedValuePath = "CustomerGroupId";
}
I got this result:
This is my database:
This usually occurs when DisplayMemberPath is set wrong, or bindable property is not a string and has no overriden ToString() method.
Try add new property to your TbCustomerGroups, for example CurrentGroupName like this
public string CurrentGroupName => Helper.CurrLang == Helper.SystemLanguage.Arabic ? CustomerGroupAName : CustomerGroupEName;
Then set cmbCustomerGroup.DisplayMemberPath = "CurrentGroupName"
Also check that CustomerGroupAName and CustomerGroupEName are strings or have ToString() method
UPDATE
Also don't use <ComboBox.ItemTemplate> if you use DisplayMemberPath
I am a beginner in Windows phone programming.
I want to bind the data from API to my XAML elements using that Binding Attributes. Please let me know how can we bind multilevel classes objects in it.
Here's my scenario.
List<Sample> SearchResult = new List<Sample>()
{
new Sample(){
Name="ABC",
modelProperty = new SampleDetail(){
articleNo="1", videoURL = "https://www.youtube.com/watch?v=abc",
colors = new List<ColorsDemo>(){
new ColorsDemo {
Name = "Red",
colorProperty= new ColorDemoProperty{ name = "ABC",article_no = "Art1",
image = new Uri("http://img.youtube.com/vi/e60E99tUdxs/default.jpg",UriKind.RelativeOrAbsolute)
}
}
}
}
}
And now, I want to bind the Name of ColorsDemo class into my textblock. See what I have done to bind in XAML like this:
<TextBlock x:Name="PName" Grid.Row="0" Margin="100,0,0,0" Tap="ProductName_Tap" HorizontalAlignment="Center" VerticalAlignment="Center" Width="350" TextWrapping="Wrap" Foreground="Black" Text="{Binding Path=modelProperty.colors.Name}" FontSize="30"></TextBlock>
From your code, i see that colors is a List of ColorDemo objects. So when you say {Binding Path=modelProperty.colors.Name}it does not tell which list item to bind to. The correct usage should be {Binding Path=modelProperty.colors[0].Name}. This tells the control to bind to the name of the first color item (as index is 0).
To bind all the colors. You should use a Listview and bind the colors in it. So you should be able to do something like this.
<ListView ItemSource={Binding Path=modelProperty.colors}>
<ListView.ItemTemplate>
<TextBlock x:Name="PName" Grid.Row="0" Margin="100,0,0,0" Tap="ProductName_Tap" HorizontalAlignment="Center" VerticalAlignment="Center" Width="350" TextWrapping="Wrap" Foreground="Black" Text="{Binding Path=Name}" FontSize="30"></TextBlock>
</ListView.ItemTemplate>
</ListView>
Disclaimer I feel like this is a fairly simple question, so i must reiterate that I did look for an answer and couldn't find anything!
Not sure if I am asking the question correctly, but I will tell you this. I am working on becoming more familiar with MVVM, so I am messing around with a ridiculously simple project of two stackpanels, ten textboxes, and some simple binding. Everything works now, since I have two panels, which separates my boxes and lets me set two datacontext.
My question is this:
a) is it possible to set the datacontext on a parent element (Stackpanel) and have half of my child elements (textboxes) use that context via inheritance and then give the other half of the elements A DIFFERENT data context?
and
b) if this is possible, how??
Thanks people-smarter-than-I
Here is the code that is trying so hard, but not really doing anything I want it to be doing:
XAML
<Grid>
<StackPanel>
<StackPanel HorizontalAlignment="Left" Margin="8,8,0,75" Width="224" DataContext="{Binding Path=Customer}">
<TextBox Text="{Binding Path=FirstName}" Height="28" Name="label1"/>
<TextBox Text="{Binding Path=MiddleName}" Height="28" Name="l2"/>
<TextBox Text="{Binding Path=LastName}" Height="28" Name="l3"/>
<TextBox Text="{Binding Path=CompanyName}" Height="28" Name="l4"/>
<TextBox Text="{Binding Path=EmailAddress}" Height="28" Name="l5"/>
<!--I want the next five TextBox elements to bind to a different datacontext-->
<TextBox Text="{Binding Path=FirstName}" Height="28" Name="label11"/>
<TextBox Text="{Binding Path=MiddleName}" Height="28" Name="l21"/>
<TextBox Text="{Binding Path=LastName}" Height="28" Name="l1lgh3"/>
<TextBox Text="{Binding Path=CompanyName}" Height="28" Name="l1hj4"/>
<TextBox Text="{Binding Path=EmailAddress}" Height="28"/>
</StackPanel>
</StackPanel>
</Grid>
Code Behind C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new MainViewModel();
}
}
View Model
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
PopulateCustomerInfo();
}
private Customer customer;
public Customer Customer
{
get { return customer; }
set
{
customer = value;
OnPropertyChanged("Customer");
}
}
private Customer customer2;
public Customer Customer2
{
get { return customer2; }
set
{
customer2 = value;
OnPropertyChanged("Customer");
}
}
private void PopulateCustomerInfo()
{
AdventureWorksLTE ctx = new AdventureWorksLTE();
this.Customer = (from c in ctx.Customers
select c).FirstOrDefault();
this.Customer2 = (from c in ctx.Customers
orderby c.FirstName descending
select c).FirstOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handle = PropertyChanged;
if (handle != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handle(this, e);
}
}
}
Well, you can do various things like changing the DataContext locally on all those lower TextBoxes:
<TextBox Text="{Binding Path=FirstName}" DataContext="..."/>
The question really is though: What do you want to achieve? Does it make sense to even set the DataContext on the StackPanel?
Maybe you should not and use a longer path instead:
<TextBox Text="{Binding Path=Customer.FirstName}" Height="28" Name="label1"/>
It all depends on what properties are going to be used in the child controls, if most or all of them are in Customer, sure, set it to keep the bindings short. If you need the main view model in some places you can use RelativeSource to get to a control with the right data context and change the path accordingly. (DataContext.*, data context appears in the path as a different source is specified)
most simple way is to have two StackPanels within the first one, grouping 5 by 5 your textboxes, first one having customer as datacontext, second one having customer2.
The stackPanel and (5 textboxes) content could even be defined as a DataTemplate (having no key or name but rather a DataType = {x:Type local:Customer } (where local is your clr namespace) ) and you would just use 2 ContentPresenters having content binding to Customer / Customer 2, that would 'automatically' display what you want.