WPF: How to use an integer (from C#-Code) inside a TextBlock? - c#

So basically I have an integer textlength and it has a number of symbols. I then want to output the number of symbols onto a TextBlock/Label, so that the user can see, how many symbols he used. Is there a way of implementing this without "Binding"? I really dont know much about binding, but if it is necessary to use it, it is alright as well!!
Here is my simple code:
C#:
...
var textlength = text.Length;
...
XAML:
<TextBlock x:Name="MyTextBlock" Width="30" Height="28" Text=" . . . " />
I want the TextBlock to operate just as a usual console --> output the value of the textlength, by saying: "The number of symbols: ..."
Thank you a lot in advance!

The easiest way of doing this is by implementing your own DependencyProperty. I haven't touched WPF in a few years, but if I remember correctly, it should look something like this:
public static readonly DependencyProperty TextLengthProperty = DependencyProperty.Register(
"TextLength", typeof(int),
typeof(YourControlType)
);
public int TextLength
{
get => (int)GetValue(TextLengthProperty );
set => SetValue(TextLengthProperty , value);
}
And the binding would look something like this:
<TextBlock Text={Binding Path=TextLength, ElementName=nameOfParentControl}/>
Then you can directly update the TextLength property, and the TextBlock will be updated automatically.
I haven't tested this code, but it should give you a rough idea of what you need to do. Also, here's the documentation about data binding and custom dependency properties.
If you really want to avoid data binding, you could manually update the content of the TextBlock in an event to reflect the new value of text.Length. But keep in mind that this is not a recommended way of doing it, and learning about bindings instead will benefit you in the future!

Related

Set NotifyOnTargetUpdated for existing binding

I have a binding in xaml <TextBlock Style="{StaticResource textStyle}" Text="{Binding DisplayText}" />.
I am attempting to write an attached behavior that reacts to the bound DisplayText value changing. If I specify NotifyOnTargetUpdated=True in the xaml, I can react to the change within the behavior and everything is fine, but I'd rather not depend on binding the Text property in a specific way just to make the behavior work.
My thought was to change the NotifyOnTargetUpdated value on the existing TextBlock.TextProperty binding when the behavior is opted in. I am using the below code to do so, where tb is the TextBlock being opted in.
var textBinding = BindingOperations.GetBinding(tb, TextBlock.TextProperty);
textBinding.NotifyOnTargetUpdated = true;
tb.SetBinding(TextBlock.TextProperty, textBinding);
The behavior is opted in like so, in the style:
<Setter Property="behaviors:Text.AutoSizeText" Value="True"/>
Initially this didn't work because textBinding was null. I can get around this by binding the Text property in xaml before the behavior property, but this still leaves an external dependency that I don't like (xaml ordering). If I do go this route, I get the below exception, which seems to indicate that I can't accomplish this in this way, at all.
InvalidOperationException: Binding cannot be changed after it has been used.
So then, how can I go about automatically handling setting NotifyOnTargetUpdated for the Text binding when the behavior is opted in?
I was able to solve my problem thanks to direction provided by #canton7. I was originally (as is often the case) looking for the way to implement my imagined solution, rather than a solution that fit my need. After adjusting my outlook, my working solution is thus:
Add the AttachedProperty InternalText to the behavior class, with a property changed handler.
private static readonly DependencyProperty InternalTextProperty = DependencyProperty.RegisterAttached(
"InternalText", typeof(string), typeof(Text), new PropertyMetadata(default(string), HandleInternalTextChanged));
In the changed handler (HandleInternalTextChanged above) do the work that I would have done in a TargetUpdated handler if my original idea to set NotifyOnTargetUpdated had worked out.
On opt-in to my behavior, create a binding from the opted-in TextBlock.Text to the InternalText attached property.
var internalBinding = new Binding { Source = tb, Path = new PropertyPath(TextBlock.TextProperty) };
tb.SetBinding(InternalTextProperty, internalBinding);
The HandleInternalTextChanged callback on InternalTextProperty allows me to work around being unable to change the NotifyOnTargetUpdated value by providing an alternate means of notifying on each change.
I prefer to bind internally to DisplayText because I'd prefer to bind to the source, if possible, rather than daisy-chain through the TextBlock.Text property.
If you need a binding to a source property, then it is created in a slightly different way.
var binding = BindingOperations.GetBindingBase(tb, TextBox.TextProperty);
if (binding == null)
{
tb.ClearValue(InternalTextProperty);
}
else
{
tb.SetBinding(InternalTextProperty, binding);
}

WPF Binding to UserControl´s DependencyProperty not working as expected [duplicate]

This question already has answers here:
How to implement two-way binding on a property?
(2 answers)
Closed 6 years ago.
I have some strange problem with DependecyProperty-binding.
To keep the question simpler i´ve created some dummy-control, that has the same unwanted behaviour
I have a UserControl, that has a DependencyProperty defined in code behind:
public static readonly DependencyProperty TestValueProperty = DependencyProperty.Register("TestValue", typeof(string), typeof(Test), new PropertyMetadata(default(string)));
public string TestValue
{
get { return (string)GetValue(TestValueProperty); }
set { SetValue(TestValueProperty, value); }
}
This property is used in XAML:
<Label Content="{Binding TestValue}" />
This control should be used in another control like this:
<views:Test TestValue="{Binding Settings.Setting123}" />
Settings is defined in viewmodel as property.
But the content of Settings.Setting123 is not visible in my usercontrol´s label.
When writing some fixes value instead of the binding it works fine:
<views:Test TestValue="Test" />
But of course i do not want a fixed value, but the content of the bound object.
Any hint what is going wrong here?
Thanks in advance!
You didn't share enough code for anybody to recreate the issue, but reading between the lines, I'm guessing that Label is in your UserControl XAML. If TestValue is a property of your UserControl, this will probably work:
<Label Content="{Binding TestValue, RelativeSource={RelativeSource AncestorType=UserControl}}" />
However, one reason you might have done that (and had it semi-work, with literal strings) is if you made your UserControl its own DataContext. In that case, then the problem is that you made your UserControl its own DataContext. If you did that, that Binding on the bound one is being evaluated in the context of the UserControl, which does not have a Settings.Setting123 property.
What a control's DataContext means, is that when you have a Binding on one of the controls properties or inside its XAML, that's where the Binding goes to look for the property you bind to. You're explicitly telling it to look in the wrong place.
If you make your UserControl its own DataContext, you can't bind anything to it. That's why you shouldn't do that. It's like one of those machines that does nothing but unplug itself from the wall. Instead, use {RelativeSource AncestorType=UserControl} bindings as above inside the UserControl XAML.
I shouldn't have to guess. You claim you created a minimal verifiable example, but didn't bother sharing it. If you share it, we can solve your problem with confidence.

In my C# XAML Windows 8.1 app, how can I iterate over my ListView?

I'm new to Windows 8.1 development, XAML, and C#, so if this question is rudimentary, please forgive me.
I've got a <Page> in my app that contains a <ListView>, like so:
<ListView ItemsSource="{Binding Mode=TwoWay}" x:Name="ListView_Statistical">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Width="100" Margin="10,20">
<Run Text="X/Y " />
<!--<Run Text="{Binding Source={StaticResource ThisPage}, Path=i}" />-->
</TextBlock>
<TextBox HorizontalAlignment="Left" Text="{Binding xVal}" PlaceholderText="X" InputScope="Number" FontSize="28" Width="100" Margin="0,10,10,10" />
<TextBox HorizontalAlignment="Left" Text="{Binding yVal}" PlaceholderText="Y" InputScope="Number" FontSize="28" Width="100" Margin="0,10,10,10" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the Code Behind, I set up its DataContext like so:
ListView_Statistical.DataContext = this.statisticalPoints;
this.statisticalPoints is defined as such:
public ObservableCollection<StatisticalPoint> statisticalPoints
{
get { return (ObservableCollection<StatisticalPoint>)GetValue(statisticalPointsProperty); }
set {
SetValue(statisticalPointsProperty, value);
NotifyPropertyChanged("statisticalPoints");
}
}
// Using a DependencyProperty as the backing store for statisticalPoints. This enables animation, styling, binding, etc...
public static readonly DependencyProperty statisticalPointsProperty =
DependencyProperty.Register("statisticalPoints", typeof(ObservableCollection<StatisticalPoint>), typeof(EnterCalc), new PropertyMetadata(0));
I'm not sure if making this a DependencyProperty is necessary, or if making it follow INotifyPropertyChanged is necessary, but they don't seem to hurt.
Anyway, so in my constructor, I add a bunch of stuff to my statisticalPoints:
this.statisticalPoints = new ObservableCollection<StatisticalPoint>();
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 1.0, yVal = 2.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 33.0, yVal = 44.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 555.0, yVal = 666.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 0.7, yVal = 0.8 });
And when I load up the page, I do indeed see five rows in my ListView, populated as defined in my initialization of this.statisticalPoints.
The part I'm having trouble with is this:
I change the first value in the first <TextBox> in the ListView, then hit my save button... but ListView.Items doesn't have my change reflected, and I can't figure out how to look at the <TextBox> itself.
What I really want to do is have this bunch of statistical points modifiable by my user and be able to save their changes. To do that, I feel like I need to read the value in the <TextBox>es, but I can't figure out how to do that.
Alternatively, if the 'right way' to do this is to keep the data in this.statisticalPoints up-to-date when changes are made in the <TextBox>es, then I thought that a TwoWay binding Mode would do it, but neither ListView.Items nor this.statisticalPoints is changed when I make changes in the <TextBox>.
I do not have event handlers set up in those <TextBox> elements, as you can see, but do I need them, or am I missing something obvious?
Thanks in advance for any help you can give me!
To solve your initial problem, make the binding for each text box Mode="TwoWay". For reasons beyond my comprehension, the mode is default OneWay on pretty much everything in Windows Store Apps.
Making the ItemsSource binding two way does next to nothing, as the UI isn't changing the collection itself (by changing, I mean completely replacing). To iterate over your collection, just iterate over this.statisticalPoints and it will have the current data.
Now, you have a ton of other misconceptions so to try and run through them:
You never showed your save button, but the bindings either update your source or they don't. A save button is usually used to persist changes from the view model to the model.
Speaking of view models, you don't appear to have one. You shouldn't be directly setting the data context of controls, and certainly shouldn't have so much in code-behind. Create a proper view model object for your page, and bind ItemsSource to a public property of that view model.
NotifyPropertyChanged on a collection usually is unnecessary unless you are replacing the collection in code.
Having it won't hurt though, except, the setter of the backing property of a DependencyProperty (DP) is never called by the framework, so putting it there is just weird
And you don't really need a DP at all. DPs are there so a parent control can bind data to your special user control. Until you are using user controls, and really understand how DPs work, you shouldn't need to use them.

Creating a variable number of TextBox'es on a WPF form

I want to create an arbitrary amount of labels and textboxes on a WPF window. Such a thing was easy to do in WinForms, and I thought I got how to do it in WPF, but I get strange results.
Namely what I want to visually happen is below (mocked). The amount and contents of the new controls is arbitrary, probably to be gotten from a text file. There's also the problem of making the form scrollable if there's a big amount of controls, but first things first.
So I named the default grid that VS creates to "grdWiz" and created the following utility function inside my window. Crude, I know, but first I want to make sure things work and beautify only afterwards. UPDATE: I now use a Canvas object instead of a Grid, and use the Canvas type instead of the InkCanvas type to try to set position. See below:
private int nInputs = 0;
private void AddInput(string defLabel, string defValue)
{
Label newLabel = new Label() { Name = "lblConf" + nInputs };
TextBox newText = new TextBox() { Name = "tbConf" + nInputs };
grdWiz.Children.Add(newLabel);
Canvas.SetLeft(newLabel, 0);
Canvas.SetTop(newLabel, nInputs * 30);
newLabel.Width = grdWiz.Width / 3;
grdWiz.Children.Add(newText);
Canvas.SetLeft(newText, grdWiz.Width / 3);
Canvas.SetTop(newText, nInputs * 30);
newText.Width = grdWiz.Width * 0.6666;
newText.Height = 30;
newText.Text = defValue;
nInputs++;
}
Inside the button click code, I do something like:
thatInitialLabel.Visibility = Visibility.Hidden;
AddInput("Main Course:", "Grits");
AddInput("Dessert:", "Apple Pie");
AddInput("Fun activity to be had afterwards:", "Sleep");
What I get is something like this:
I'm doing something obviously wrong, but I don't know what. Also, I will no longer emit opinions on the relative merits of GUI frameworks. Suffice it to say I'm one of these.
Well, you got the source of the problem right: WPF is not WinForms.
Without seeing the parent XAML, I can't say for sure what your current problem is. You are using attached properties that may not have any effect without the correct parent control. That being said, there is a much easier way.
First, create a class that models your data; say:
public class Input
{
public string Label {get; set;}
public string Value {get; set;}
}
Without going through how to set up MVVM: MVVM: Tutorial from start to finish?
Do this:
<ListView ItemsSource="{Binding InputCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Label}"/>
<TextBox Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This does the following:
Sets up a ListView that populates off of the "InputCollection" property in your ViewModel, which is likely an ObservableCollection<Input>
Makes each item a horizontal stack panel that
a. Has a text block bound to the "Label" property of the item
b. Has a text box bound to the "Value" property of the item
Take that compared to the equivalent WinForms code. I would argue that it is much clearer, easier to maintain and understand, and overall, is much better practice. I would strongly disagree that life was "easier" with WinForms in this instance.

Data binding the Text property of a Run

I'm trying to databind a Run of a TextBlock in code at runtime, and I can't for the life of me figure out how.
Several sources on the internet suggest this isn't possible without some (not too pretty) additional workarounds, and, more importantly, it should completely fail when you try to do it in XAML.
Yet, in my application I have the following, which works beautifully:
<DataTemplate x:Key="PitchTemplate">
<Grid Width="120" Height="120" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center">
<Run Text="{Binding}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="36"/>
<LineBreak/>
<Run Text="{Binding Frequency, StringFormat=\{0:n2\}Hz}" FontFamily="{StaticResource PhoneFontFamilyNormal}" Foreground="{StaticResource PhoneSubtleBrush}"/>
</TextBlock>
</Grid>
</DataTemplate>
So I figured: If it can be done in XAML, it should be possible to do it in code.
Sofar, to no avail. Using the "regular" way of binding in code won't work; the Run class doesn't inherit from FrameworkElement, so doesn't have a SetBinding method, and it's Text property is not a DependencyProperty.
Using BindingOperations.SetBinding doesn't work because the Text property is not a DependencyProperty.
I'm up to the point that I'm willing to accept that it can't be done at runtime (although not without a last attempt at StackOverflow), but I'm still curious if
This can be done in code at runtime?
And if not:
How come it does work in XAML?
EDIT:
The example shown is just there to show that it can be done in XAML. The reason I need to create the bindings in code, is that I have a control that dynamically creates other elements, which need to be data bound.
UPDATE:
As Pete and I both found out, there is a dependency property for Text, but it's private. I assume that's why it does work through XAML (the xaml parser probably has more rights when it comes to reflection, and more knowledge in general about classes).
The upside is, that this means (tried & tested) it also works through XamlReader.Load(), which is (sofar) the cleanest solution I've come up with.
But if anyone has anything better, I'd be glad to hear about it.
Run.Text is backed by the private TextProperty which means you can't directly set its value without some reflection gimmicks, something like this:
Run r=new Run();
r.Text = "Moo";
var field=r.GetType().GetField("TextProperty", BindingFlags.Static | BindingFlags.NonPublic);
var dp=field.GetValue(null) as DependencyProperty;
BindingOperations.SetBinding(r, dp, new Binding {...});
This is rather ugly, but perhaps it can be useful.
You can find various workarounds for this. This SO post uses a custom attached property to configure binding. The attached property is used because Run is a sealed class in Silverlight so you can't create you own Run that supports binding.
The reason that it works in XAML but not in code-behind might be that there is a dependency property for Text, but it is private. It's a little bit 'black magic' to me though, so that's just a guess! This is a strange one, as in WPF the Run does inherit from FrameworkContentElement and has a SetBinding method...
Could you create a subclass of Run that contains a public DependencyProperty for text? I'm afraid I don't have the Silverlight dev tools to hand to try it out at the moment, but I'll try and take a look later.
Thanks both to Pete & Panagiotis for their efforts and suggestions (both 1 up).
In the end, I decided to go with my own solution (found in the "Update" section of the question): Create dedicated XAML strings containing the Run including the binding, and use XamlReader.Load() to parse it, and return a Run object.
The situation I'm working on is quite specific, so a local solution to the problem is good enough (for now). Reflection, as suggested by Panagiotis, won't work due to restrictions imposed by Silverlight. Lastly, the BindableRuns solutions would need either extensive work to deal with nested properties, or I would have to "uglify" my view model, so I discarded it (also for now).
Thanks all for your input.

Categories