WPF - xaml's binding not working after obfuscation - c#

I made an application in WPF (.Net 4.7). For code protection, I am using .Net Reactor.
I am using following syntax to bind textblock to first element of list in my model.
<TextBlock Text="{Binding list[0].property1}"/>
This code works fine but after obfuscation, this code no longer works. I changed it to this
<WrapPanel DataContext="{Binding Path=list}">
<TextBlock Text="{Binding property1}"/>
</WrapPanel>
and this bind textblock to 'property1' of list first element (what I needed). I accidentally got this solution, I don't know if it is a proper way or not. I build application without obfuscation and above also works. Questions I have regarding this are
Why binding is not working like usual after obfuscation?
Using this method, does it always bind to first element of collection? If it always bind to first element, than I will continue deployment of application without wasting more time.
Sorry for bad English!!

Related

Inline c# in XAML front-end: Why doesn't this work in XAML?

Scenario: Developing a "thick" client in UWP, using c# destined for the desktop of Windows 8.1/10 (a store app).
In my user interface, I tried to use the following inline
<StackPanel Grid.Column="0">
<TextBlock x:Name="Qty_Size_Crust" Margin="25,10,20,0" Padding="0,0,0,0" TextAlignment="Left" TextWrapping="Wrap" MaxHeight="25" Foreground="#ff230909" FontFamily="Segoe UI" FontSize="16" xml:space="preserve" HorizontalAlignment="Left">
if ({Binding WidgetName} == "Backend")
{
<Run Text="{Binding ItemCode, Converter=ItemCodeToDescriptionConverter}"/>
}
else
{
<Run Text="See Administrator}"/>
}
</TextBlock>
The compiler would accept this, but during execution, I would neither see Item Description nor See Administrator. Is there something else I should be doing in order for this work? I was able to work around this by putting the code in the ViewModel, but wondered why this didn't work (or maybe it shouldn't).
XAML doesn't work with inline scripts so the answer is simply - it's not supported.
In your example - the condition code is simply added as text runs in the TextBlock, so the compiler accepts that.
As for the best solution to your specific problem - a condition in the view model is a good one. A TemplateSelector would work on the view side, but requires more code. View model might actually also be the best place to put it if you want to be able to test it, so it really is the right way to do it. Putting logic in the view layer would simply add code that can't be unit-tested.

Is it possible to bind to a property on the container of the Adorned Element?

In my WPF app we are using an adorner for displaying validation messages, in the particular case there is a single row grid that has multiple controls some of which have validation. The problem I'm having is that I want to force the width of the error message control to be the same as the grid but can't seem to find a way to reference that grid from the adorner template. Here is a sample of what I tried:
<ControlTemplate x:Key="Local_TopAdornedTemplateWide">
<StackPanel>
<AdornedElementPlaceholder x:Name="adornedElement"/>
<TextBlock MaxWidth="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType=Grid}, ElementName=adornedElement}"
TextWrapping="Wrap"
Text="{Binding Converter={StaticResource Local_ValidationErrorMessageConverter}}"
Style="{DynamicResource Error_Text}"
Padding="2 1 0 0"
Visibility="{Binding ElementName=adornedElement, Mode=OneWay, Path=AdornedElement.IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
</StackPanel>
</ControlTemplate>
This causes the application to crash with an XamlParseException.
Ideally the solution would not be specific to a grid so that it would get the width of any container type, but for now grid is the only use case.
Edit:
Here is an example of another template we use in the application; this template would not work for my case as it would limit the error to be the width of a single column of the aforementioned grid:
<ControlTemplate x:Key="Local_TopAdornedErrorTemplate">
<StackPanel>
<AdornedElementPlaceholder x:Name="adornedElement"/>
<TextBlock MaxWidth="{Binding ElementName=adornedElement, Path=ActualWidth}"
TextWrapping="Wrap"
Text="{Binding Converter={StaticResource Local_ValidationErrorMessageConverter}}"
Style="{DynamicResource Error_Text}"
Padding="2 1 0 0"
Visibility="{Binding ElementName=adornedElement, Mode=OneWay, Path=AdornedElement.IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
</StackPanel>
</ControlTemplate>
Using snoop I captured the following two screenshots (I could not take one of the full stack to prevent posting anything proprietary)
This shot shows the grid I mentioned previously, within this it is the FinancialTextBox item that is being adorned
This shot shows two things, the item selected in blue is the highest ancestor of the grid in the previous shot, the yellow highlight is the Textbox from the content template
With those two it seems to be apparent that (based on information from Contango's answer) the two items aren't not in the same visual tree which would lead me to believe my question is not possible. However the second template I added (which does work) points that at least some visual information from the adorned element lives on in the place holder.
So now my question boils down to a) does this information include the parent of the adorned element and b) how can this be accessed via a binding on a different element?
This ended up being a lot simpler than the path I was trying to go down.
I was doing some reading on the AdornedElementPlaceholder class and came across this entry on MSDN and noticed that the class actually has a property called parent, with that I tried the following binding and it works perfectly:
MaxWidth="{Binding ElementName=adornedElement,
Mode=OneWay,
Path=AdornedElement.Parent.ActualWidth}"
WPF is quite powerful and flexible.
You can bind any property in any XAML tag to any property in any other XAML tag.
For example, you could write a test app that binds the Text property of an input box to the Text property of a label, so as you type something into the text box, the label would change automatically (assuming you use UpdateSourceTrigger=PropertyChanged). This is a direct XAML to XAML binding, with no C# in sight.
Similarly, you could bind the width of your error box to the width of the parent control, whatever that may be.
Google RelativeSource and AncestorType, this is a great link:
http://druss.co/2013/10/wpf-binding-examples/
See if you can grok how the Visual Tree and Logical Tree works in WPF, once you understand that, you will understand more of how binding works.
I'd also recommend using the free tool Snoop to look at the Visual Tree. XAML Spy is excellent, but not free.
Snoop can tell you if there is anything that has a bad binding at runtime (you set the filters up, and it will list all bad bindings).
You can use Snoop to get the full XAML path of your source (the XAML you wrote above), then get the full XAML path of the target (i.e. the ActualWidth of your Grid), then compare them: it may be quickly apparent that one is not the ancestor of the other, as they are on different branches of the visual tree, or that there is some other issue which is preventing a simple walk up the visual tree from working.
If you just want to get something working, as a proof of concept, try naming the target XAML grid using x:Name, and reference it by name instead of AncestorType.

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.

Can I implement ScrollToHorizontalOffset() functionality in XAML? (for a dynamic list)

Here's the problem: I have a data-bound list of items, basically a way for users to map a request to a response. The response is an xml-based file. I'm letting them queue these up, so I've used a combobox for responses. The responses will include the full path, so they get a bit long. I want the displayed text of the combobox to be right-justified so the user can see the file name. For my static controls, I just use ScrollToHorizontalOffset() when a file is loaded and I'm done. For this dynamic list, I'd like to do it in xaml.
The "somewhat ugly" solution would be to store all the ComboBox objects as they load... then I can call ScrollToHorizontalOffset() directly, but I'd really prefer to do it a cleaner way than that! EDIT: (Actually, this may not be reasonable. A quick look at trying to hack around this issue gets into some really awkward situations trying to map my datasource items to the controls)
I've tried HorizontalContentAlignment, that only impacts the "dropped-down" portion of the ComboBox.
I've also tried to hook other various loading events, but haven't found one that works.
Using an Item template you can decide what will be shown.
You can set tooltip. You can then also use converters to add the dots.
<ComboBox x:Name="ConfigurationComboBox" VerticalContentAlignment="Center" ToolTip="saved configuration" SelectionChanged="ConfigurationComboBox_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding}" ToolTip="{Binding Path}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
To measure the text, see Measuring text in WPF.

Binding with a converter inside a template

I have defined a DataTemplate for a ListBox. Inside the template, I use TextBlock controls to display the properties of the data context. For example:
<TextBlock Text="{Binding Path=FirstName}" />
And if I do this:
<TextBlock Visibility="{Binding Path=IsAccountValid}" />
...the application runs, but there is a warning in the output about trying to bind a boolean property to a Visibility enumeration.
If I do this:
<TextBlock Visibility="{Binding Path=IsAccountValid,Converter={StaticResource visibilityOfBool}}" />
and somewhere in my App.xaml is:
<BooleanToVisibilityConverter x:Key="visibilityOfBool" />
I get a null reference exception.
I suspected this might be because the property IsAccountValid is not a dependency property, so I added a CheckBox to the window, and did this:
<TextBlock Visibility="{Binding Path=IsChecked,Converter={StaticResource visibilityOfBool},ElementName=butA}" />
But got the same error.
Why? The DataContext object is valid because if I bind IsAccountValid to the Text property, the value is correctly displayed.
The converter is never called, so I am wondering if it is the converter that cannot be found.
Why can the converter not be found? Why can the converter be found outside the data template and not inside the data template?
I tried building the template again with Blend, as Blend usually gets it right, but the code it generated was the same as mine.
I tried some of the fixes suggested on this website, including setting RelativeSource to TemplateParent and Self, but it made no difference.
What is going on?
We use such converter all the time in our Data Templates. Do you define the converter key inside your resource dictionary? Merge another Resource Dictionary?
The IsAccountValid property doesn't have to be a dependency property. If the converter couldn't be found then you wouldn't be able to open the form. You have the right approach using the converter but it is difficult to say exactly what is causing the exception without seeing more information.
As Amittai and Chris pointed out, it seems that you're headed in the right direction. I know it sounds a bit stupid, but try to add a space between the comma and the Converter= statement in the binding. Like so:
<TextBlock Visibility="{Binding Path=IsAccountValid, Converter={StaticResource visibilityOfBool}}" />
On some systems there are weird symptoms when there's no space after the comma. I couldn't find the actual reason for that.
Thank you all for your help in my investigation.
I have solved the problem.
These two lines of code are included in the DataTemplate, one is a TextBlock, and one is a hyperlink:
<TextBlock Text="Hello" Visibility="{Binding IsChecked,ElementName=chkBox,Converter={StaticResource visibilityOfBool}}" />
and
<TextBlock Grid.Column="1" >
<Hyperlink Click="ProgHomePageHyperlink_Click" >
<TextBlock Text="{Binding Path=Title}" />
</Hyperlink>
</TextBlock>
When they are both included in the code, the runtime throws a null reference exception.
But if I comment one of them out, either the TextBlock or the HyperLink, everything runs ok.
If I remove the Click handler from the hyperlink, everything runs ok.
If I comment out the converter in the TextBlock, the application runs, but I get a mismatched binding warning, which is well deserved.
So, including a Click handler in the hyperlink means the converter in the TextBlock cannot be found.
How weird is that!

Categories