Set TextBlock Content by bound C# property (WPF and Caliburn.Micro) - c#

If it helps to know, I'm using Caliburn.Micro, and have laid out everything based on the MVVM framework requirements as I understand them.
Here is the relevant XAML ...
<ListView ItemsSource="{Binding ProductListBox}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ProductID}"/>
<TextBlock Text="{Binding ProductDescription}"/>
<TextBlock Text="{Binding ProductDescriptionExtended}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The above content of the ListView is created dynamically based on the content of ProductListBox, which sits inside this class...
public class MainViewModel : Screen
{
public List<ProductModel> ProductListBox { get; private set; }
public void GetProductsButton()
{
DBAccess db = new DBAccess();
ProductListBox = db.GetProducts(SearchTextBox);
NotifyOfPropertyChange(() => ProductListBox);
}
}
That List is populated when a Button is clicked, it causes the above GetProductsButton() method to start.
Over in the DBAccess class, in the GetProducts method I would like to change the format of the text in the ProductListBox<>.ProductDescription. Its a string, but I'm happy to change it to any type should it help the cause!
The kind of change I would like to achieve is simply (highlighting) changing the background color of selected text based on found search terms the user had typed in, that the bound XAML TextBlock will then display.
What I cant work out is how to highlight any text at this time via C#, that will then be displayed purely by the bound XAML control... I've only been able to do it by hardcoding XAML which is not going to help for what I'm trying to achieve.
In the DBAccess class and inside the GetPeoducts method, this is the kind of thing I have tried in order to make this happen...
I've pasted in this code...
TextBlock textBlock1 = new TextBlock();
textBlock1.Inlines.Add(new Bold(new Run("TextBlock")));
textBlock1.Inlines.Add(new Run(" is designed to be "));
textBlock1.Inlines.Add(new Italic(new Run("lightweight")));
textBlock1.Inlines.Add(new Run(", and is geared specifically at integrating "));
textBlock1.Inlines.Add(new Italic(new Run("small")));
textBlock1.Inlines.Add(new Run(" portions of flow content into a UI."));
Which although doesn't change background color, will lead to that if I could make it work ;)
I think in every example of the kind of code I've quoted above, the writer always ends that code with something like this...
this.Content = textBlock1;
And the examples I've seen also always seem to inherit from the Window class. I don't want to do it this way. I want to find my search terms, highlight the given text and allow the bound TextBlock to update itself based on the populated List of type ProductModel.
I've tried changing the type of ProductDescription to a TextBlock, and used the above code to allow my XAML to remain bound directly to it
ie..
PM.ProductDescription = textBlock1;
In this case there was no GUI output at all for the content.
I've also tried straight HTML-like formatting to the string type
ie..
PM.ProductDescription = "<bold>Hello World</bold>";
This gives the literal text output of <bold>Hello World</bold> on the TextBlock control though.
I've also tried changing the type of Binding key from Text to other things in the hope I might work it out, without success.
Any ideas or help would be greatly appreciated, thank you!
Here is a little picture of what I would like to see the program be able to do..

Related

How to preview what a XAML control with bound values looks like

I'm writing an app in XAML, and I'm using binding for getting values to the UI layer. I'd like to see what my control will look like while making changes to the XAML, but because the data values are bound, many areas show up as blank (which, in turn, messes up the relative layout).
Is there any way to give XAML values to use for rendering the control review without replacing the Binding directives?
You can also use design time data to see how your xaml works. You just need to add new class that will be treated as design time view model. Its more elegant way to test xaml at design time.
Maybe you can set TargetNullValue or FallbackValue property in your binding, example:
<TextBlock Text="{Binding NotExsitOrNullPropertyName, TargetNullValue=SomeDefaultValue, FallbackValue=SomeDefaultValue}" ></TextBlock>
Hope it hepls.

WPF TextBox Background binding doesn't work

I am doing TextBox binding as shown below. But the background color doesn't change. Any help is appreciated!
<TextBox x:Name="FirstNameTextbox" Text="Test" Background="{Binding Path=FirstNameBackground,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
C# code:
public SolidColorBrush FirstNameBackground
{
get
{
return firstNameBackground;
}
set
{
firstNameBackground = value;
OnPropertyChanged("FirstNameBackground");
}
}
A couple things.
A). Have you implemented INotifyPropertyChanged, without it binding to the xaml will only work one way and then stop, since there is no way to notify when property has changed.
B). Your xaml is not properly built. You must have a closing bracket at the end of textbox such as
<textbox/>
or
<textbox></textbox>
C). How is your datacontext set? If it is not set to anything , this will not work. This can be done by
a. datacontext =this in your codebehind
b. settings datacontext in xaml using window.datacontext as the xaml key
D). The information given is very very vague and i can only make decisions and suggestions based on common mistakes I have seen when building xaml/wpf apps. Please provide more information on
1. How datacontext is set.
2. How full xaml looks like
3. full codebeind/viewmodel if applicable

"Dynamically" Generate a WPF View C# for Plotting with DynamicDataDisplay

I am writing a WPF application in C#. This application is design using MVVM.
Currently, I have a parent window with a few check boxes. Use user can check whichever boxes they want and then click the "plot" Button. Once they click "plot", a new child window comes up displaying the data on a single graph.
So, if I have only 1 check box checked, and then click "plot", I will see a graph with a single line on it. If I have 2 check boxes check and click "plot", I will see the same single graph, but it will have 2 lines on it.
My current Implementation:
Currently, I have a "view" class called GraphWindowView. The view obviously needs to know of which data to show. So to do that, I have dependency properties GraphWindowView.Dates and GraphWindowView.Data which ultimatley produces a graph of Data (y axis) vs. Dates (x axis).
Question: This current implementation of GraphWindowView is obviously restricted to only being able to graph one set of data (i.e. Data vs. Dates). I would like to make this (a lot) more extensible and have an arbitrary number of plots available depending on how much check boxes are checked. How would I go about doing this? I think I need to rethink my use of dependency properties...
>>> UPDATE
So I made a GraphLine class which should represent a line on the graph. The "graph" is actually a ChartPlotter element in the GraphWindowPresenter.xaml class. Additionally, I specified a DataType for the GraphLine objects, but that is all I understand. What are the next steps to this, how do I actually add the data to the graph? And how/where do I make instances of GraphLine to populate the ChartPlotter element? Sorry I am pretty lost on this, even after reading quite a few tutorials. Thanks for all the help so far, I really appreciate it!
GraphWindowView.xaml
<Window x:Class="BMSVM_Simulator.View.GraphWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:BMSVM_Simulator.ViewModel"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
x:Name="ThisGraphWindowInstance"
Title="Plot" Height="500" Width="750"
Icon="../res/qualcomm_q_icon.ico.ico"
MinWidth="400" MinHeight="300">
<Window.DataContext>
<ViewModel:GraphWindowPresenter/>
</Window.DataContext>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:ChartPlotter.HorizontalAxis>
<d3:HorizontalIntegerAxis Name="dateAxis"/>
</d3:ChartPlotter.HorizontalAxis>
<d3:ChartPlotter.VerticalAxis>
<d3:VerticalIntegerAxis Name="countAxis"/>
</d3:ChartPlotter.VerticalAxis>
<d3:Header FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis}"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=xAxis}"/>
</d3:ChartPlotter>
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:GraphLine}">
<!--WHAT GOES HERE-->
</DataTemplate>
</Window.Resources>
</Window>
GraphLine.cs
namespace BMSVM_Simulator.ViewModel
{
class GraphLine
{
public string xAxis { get; private set; }
public string yAxis { get; private set; }
public string title { get; private set; }
public string legend { get; private set; }
public EnumerableDataSource<int> data { get; private set; }
public EnumerableDataSource<int> dates { get; private set; }
}
}
Most of these types of problems in WPF can be sorted out by some careful use of data binding and DataTemplates, rather than miles of procedural code. The general idea is that you create a custom class with all of the properties that are required to draw all of your lines. You would then declare a DataTemplate to define how the various properties are to be data bound, perhaps a little something like this:
<DataTemplate DataType="{x:Type YourXamlNamespacePrefix:GraphLine}">
<Line X1="{Binding X1}" Y1="{Binding Y1}" X2="{Binding X2}" Y2="{Binding Y2}" />
</DataTemplate>
Then you create a collection of your custom class instances and data bind it to some collection control, like an ItemsControl and each one will be automatically rendered in the correct location:
<ItemsControl ItemsSource="{Binding YourGraphLineCollection, RelativeSource={
RelativeSource AncestorType={x:Type YourXamlNamespacePrefix:YourControlName}}}" />
Welcome to the powerful world of WPF data binding and DataTemplates.
UPDATE >>>
The custom class to data bind to the Line elements is not a view model. Think of it as a data type class, for which you will declare a DataTemplate like the one above. When I said that it should have all of the required properties, if you look at the above example, you'll see that it would at least need four double properties to data bind to the four used properties of the Line element. However, you might also choose to add further properties to data bind to the Stroke, StrokeThickness or Fill properties for example.
As for where you should define the DataTemplate, it should be within scope of the items that have it applied. If you want to use it in one view, then put it in the UserControl.Resources section of that view. However, if you want to use the same DataTemplate, then you should put it into the Application.Resources section of the App.xaml file because those Resources are available application wide.
FINAL UPDATE >>>
As noted in my comment, teaching users how to use WPF is definitely out of scope for this website, so I won't be doing that. To learn about DataTemplates, you should read the Data Templating Overview page on MSDN. When you don't know about something, MSDN should always be your first place to search for answers.
I can give you a few last tips before I go: The DependencyProperty in your control should be of type ObservableCollection<GraphLine>. Inside your control, you should data bind them to some sort of ItemsControl as shown above - I changed the Binding Path in it because you should really use a RelativeSource Binding to locate the property in your situation (where YourControlName is the name of your UserControl where you want to draw the Line objects).
Finally, in your view model (that is linked with the view that contains your new UserControl that draws the lines), you'll need a collection property to data bind with the collection in the UserControl, let's say named YourGraphLineCollectionInViewModel:
<YourXamlNamespacePrefix:YourControlName YourGraphLineCollection="{Binding
YourGraphLineCollectionInViewModel}" />
It's in this view model that you add the instances of your GraphLine class into the YourGraphLineCollectionInViewModel collection and as long as you have set up your Binding Paths as shown here, they'll appear in your UI within the ItemsControl. I am assuming that you know how to correctly set your DataContext - if not, you can easily find out how to do that online.

how to create binding in code when the datatemplate is declared in xaml

i have a datatemplate declared in xaml.
for e.g.
<DataTemplate x:Key="TestTemplate">
<StackPanel>
<TextBox Name="txtBox" Visibility="Visible"></TextBox>
</StackPanel>
</DataTemplate>
I wish to set the binding for txtBox in code behind before the element is generated because i have different binding paths for different elements that get generated
I can get the template in the code behind as :
DataTemplate tmplt = FindResource("TestTemplate") as DataTemplate;
but i am not sure what to do next. How to get the the txtBox reference to set the binding.
We have to remember one thing that Templates are not instantiated UI controls. They are streamed obejcts in XAML and are shared between UI elements. So if you edit a dataTemplate and change its stucture (by adding, editing, deleting an element under the template) it would change the one data template which is shared among controls. Thus other elements using that template will also be affected by the change.
Now lets address your issue of adding a dynamic biding to a textbox. You say each generated textbox will have different binding paths. So this definitely does NOT call for changing the data template itself!
You will have to access the text box and add dynamic bindings to it AFTER the textbox's is generated.
I see that your binding differs based on your "situation", so why cant you use TemplateSelector? Template selector will decide which data template (having one specific binding applied to the TetxBox) at runtime.
The first part of answer - is FindName() method.
example:
DataTemplate tmplt = FindResource("TestTemplate") as DataTemplate;
TextBox my = (TextBox)tmplt.FindName("txtBox");
try out this, it should help to get access to TextBox control. I think that you know how to bind to. If you want your DataBinding behave different way, use MultiBinding and Converter.
EDIT
public class GeneralObject
{
private object someObject;
public GeneralObject(object initObject)
{
this.someObject = initObject;
}
//If you want to bind to some text, for example
public string Text
{
get
{
//I think you know which objects are coming as input
if (this.someObject is SpecialClass1)
return ((SpecialClass1)this.someObject).SpecialClass1TextProperty;
if (this.someObject is SpecialClass2)
return ((SpecialClass2)this.someObject).SpecialClass2TextProperty;
//and so on.
}
}
}
EDIT 2
One more possible way
So I remember, that WPF have ContentControl!
<ContentControl Content="{Binding Path=CurrentObject}"/>
But in this case you have to create number of DataTemplate's, every Template for one class.
<DataTemplate DataType="{x:Type local:SpecialClass1}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:SpecialClass2}">
...
</DataTemplate>
<!--and so on-->
WPF resolve DataTypes of ContentControl.Content property, and put to the ContentControl right DataTemplate.

Set TextBlock to be entirely bold when DataBound in WPF

I have a databound TextBlock control (which is being used inside a DataTemplate to display items in a ListBox) and I want to make all the text in the control bold. I can't seem to find a property in the properties explorer to set the whole text to bold, and all I can find online is the use of the <Bold> tag inside the TextBlock, but I can't put that in as the data is coming directly from the data source.
There must be a way to do this - but how? I'm very inexperienced in WPF so I don't really know where to look.
Am I missing something, or do you just need to set the FontWeight property to "Bold"?
<TextBlock FontWeight="Bold" Text="{Binding Foo}" />
Rather than just having a TextBlock, try this:
<TextBlock>
<Bold>
<Run />
</Bold>
</TextBlock>
Then databind to the Run.TextProperty instead.
You say that the data is coming directly from the datasource; is it possible to place a layer of abstraction in front of it? Its quite common to create a View for what you are displaying, and have the View communicate with the data. The most common implementation of this idea is Model View View-Model (MVVM). Have a read about it online.
You might have a 'DisplayText' property that is bound to the textbox, and it is simply a 'getter' that wraps the underlying text. It can detect if the text is already wrapped in and if not, wrap it.
Eg.
public class TestView {
private Test datasource;
public TestView(Test source)
{
this.datasource = source;
}
public string DisplayText {
get {
if (datasource.Text.Contains("<bold>")==false) {
return "<bold>" + datasource.Text + "</bold>";
}
return datasource.Text;
}
}
}
Then, bind to the View instead of directly to the object.

Categories

Resources