How To Bind Image Source in C# (MVVM) - c#

In My Project,I Create Some Images with C# and i want these photo's sources, Bind to a property in My ViewModel.
in My MVVM :
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string _Light= "dark.png";
public string Light
{
get { return _Light; }
set {
_Light = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Light)));
}
}
in My C#:
BindingContext = new LightViewModel();
LightViewModel light = new LightViewModel();
Image dark = new Image { Margin = new Thickness(0, -5, 0, 10), HeightRequest = 20, WidthRequest = 20 };
dark.SetBinding(Image.SourceProperty, light.Light);
i use exactly this MVVM with this Xaml, and it's Property work
<Image Source="{Binding Light}" ></Image>
Can help me :)

Actually, the second parameter of SetBinding method is the name of the property, not the property itself, so what you should do is something like this:
dark.SetBinding(Image.SourceProperty, "Light");

Related

Why Can't Won't My WinForms Textbox bind?

Basically, I want to bind a textbox in my xaml with a variable, DisplayNumFilter, in my c#. I would like to initialize my textbox to 20. I've been looking through several stack overflow posts and have tried many things, which is the constructor is kind of a mess (I tried many things and just sort of left them there). However, nothing's worked. My apologies with any mistakes in regards to formatting or terminology, I am still very new to this.
Here is a snippet of my xaml:
<TextBox Name = "NumAccounts"
Text="{Binding Path = DisplayNumFilter, Mode=TwoWay}" />
Here is a snippet of my c# code:
private string _displayNumFilter;
public string DisplayNumFilter{
get => _displayNimFilter;
set{
_displayNumFilter = value;
OnPropertyChanged("DisplayNumFilter");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName){
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public constructor(){ //it has a different name, I just use this as an example
DisplayNumFilter = "20";
InitializeComponent();
Binding binding = new Binding();
binding.Path = new PropertyPath("DisplayNumFilter");
binding.Source = NumAccounts;
BindingOperations.SetBinding(NumAccounts, TextBox.TextPoperty, binding);
NumAccounts.Text = DisplayNumFilter;
}
The XAML markup Text="{Binding Path=DisplayNumFilter}" tries to bind to a DisplayNumFilter of the current DataContext of the TextBox control so you need to set the DataContext to an instance of the class where the DisplayNumFilter is defined.
This means that your constructor should look something like this:
public constructor() {
DisplayNumFilter = "20";
InitializeComponent();
DataContext = this;
}
There is no reason to create a Binding object programmatically if you use setup the bindings in the XAML markup.
There are some issues with your code, but I will focus on the main:
Your binding source is wrong. From that the binding source it will start with the property path. To resolve the property DisplayNumFilter the source has to be set to this.
public SomeWindow()
{
DisplayNumFilter = "20";
InitializeComponent();
Binding binding = new Binding();
binding.Path = new PropertyPath("DisplayNumFilter");
binding.Source = this; // -> was before NumAccounts;
BindingOperations.SetBinding(NumAccounts, TextBox.TextPoperty, binding);
NumAccounts.Text = DisplayNumFilter;
}

Setting the colour of a LiveCharts Series that has custom labels using XAML Binding

The typical way of setting the Series Fill and Stroke are explained perfectly on the Livecharts website. However, in order to set custom labels for points you need to create the Series in the view model (Shown below). This prevents you from being able to call Fill or Stroke in the XAML as you don't have each series being created like the example below.
<lvc:CartesianChart Name="Chart" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="15">
<lvc:CartesianChart.Series>
<lvc:LineSeries Values="{Binding Values}" LineSmoothness="1" StrokeThickness="10"
DataLabels="True" FontSize="20" Foreground="#6B303030"
Stroke="White" Fill="Transparent" PointGeometrySize="0"/>
</lvc:CartesianChart.Series>
My current code which creates the series and its associated labels.
ViewModel
ABValuesSC = new SeriesCollection
{
new LineSeries
{ Values = ABValues,
DataLabels = true,
FontSize = 14,
//MinPointShapeDiameter = 15,
StrokeDashArray = new System.Windows.Media.DoubleCollection {2},
Fill = System.Windows.Media.Brushes.Transparent,
LabelPoint = point =>
{if(point.Key==0)
{
return "A";
}
else
{
return "B";
}
}
},
new ScatterSeries
{ Values = TriggerValues,
DataLabels = true,
FontSize = 14,
MinPointShapeDiameter = 15,
LabelPoint = point =>
{if(point.Key==0)
{
return "1";
}
else
{
return "2";
}
}
},
new LineSeries
{ Values = NAVmatValues,
LineSmoothness=0,
}
};
XAML
<lvc:CartesianChart Series="{Binding ABValuesSC}"/>
Giving you this output.
Is there a method for accessing a series fill for the chart to change it from the default and have it be bindable? for example would it be possible to have the colours be bound to a list
or is there a better way of making the labels for my chart such that i can use a similar method to the example at the top of this post?
Instead of creating a SeriesCollection programmatically and bind it to the View, its possible to define (most) of these Things directly in the XAML and only bind the Things you need to change in your ViewModel.
Move to XAML
As far as I understood your code, you only want to change the Values and the Fill in your ViewModel, so we put your "configuration" in the XAML which looks something like this:
<lvc:CartesianChart>
<lvc:CartesianChart.Series>
<lvc:LineSeries Values="{Binding ABValues}" DataLabels="True" FontSize="14" StrokeDashArray="1,1" Fill="{Binding ABColor}" LabelPoint="{Binding ABLabelPoint}"/>
<lvc:ScatterSeries Values="{Binding TriggerValues}" DataLabels="True" FontSize="14" MinPointShapeDiameter="15" LabelPoint="{Binding TriggerLabelPoint}"/>
<lvc:LineSeries Values="{Binding NAVmatValues}" LineSmoothness="0"/>
</lvc:CartesianChart.Series>
</lvc:CartesianChart>
LabelPoint Binding
The LabelPoint cant be set (or at least i dont know how) in the XAML and must be provided as a Property in your ViewModel (see Code below)
class YourClass
{
//Property to Bind
public Func<ChartPoint,string> ABLabelPoint { get; set; }
//Constructor
public YourClass()
{
//Define LabelPoint, where 0 = A, 1 = B etc.
//Or use your Code, doesent really matter
ABLabelPoint = point => ((char)(point.X + 65)).ToString();
}
}
(Dont forget to do this for the Scatterseries LabelPoint as TribberLabelPoint Property)
Values Binding
The Values are now bound, therefore you must expose them as a Property like this
public ChartValues<ValueType> ABValues { get; set; }
Note: Replace ValueType with the used Type, eg. int or byte.
Fill Color Binding
Like the Values, the Fill color is bound to a Property which must be implemented. Make sure you notify the View when the Color Changes (see INotifyPropertyChanged)
If your class already has this interface implemented it could look like this
//private Field
private SolidColorBrush _abColor = new SolidColorBrush(Colors.Green);
//Public Property which the XAML binds to
public SolidColorBrush ABColor
{
get { return _abColor; }
set
{
_abColor = value;
OnPropertyChanged();
}
}
As you already use MVVM, use a command to manipulate color. In the Command delegate all you have to do is to access the series collection and pick out the series you want to change. Note that you have to cast it to the right series type.
((LineSeries)ABValuesSC [0]).Fill = Brushes.Aqua; //change fill of first series
This way you can manipulate any property of the series you want, not just fill.

Xamarin.Forms set focus from mvvm ViewModel

I'm working on a chat application using Xamarin.Forms.
And I want to avoid to hide the keyboard when the Entry loses focus and button Send is clicked.
How can I do it on Android and iOS?
I use XF, full Mvvm without XAML(only C#)
Updated:
In page class:
private EntrySetBorder _newMessageEntry;
...
_newMessageEntry = new EntrySetBorder
{
TextColor = Color.Black,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.End,
Margin = new Thickness(0, 0, 5, 0)
};
In model class:
var entry = CurrentPage.FindByName<EntrySetBorder>("_newMessageEntry");
entry.Focus();
}
This can be achieved easily by using the FindByName<>() function inside the PCL.
This is one way of doing that:
Entry myEntry = CurrentPage.FindByName<Entry>("YourEntryName");
myEntry.Focus();
You can add that at the end of the click handler of your send button.
Edit:
In your case I think your problem is that your entry is set to private, so I would suggest either expose it as public or expose it using another public property. Two solutions that might work:
public EntrySetBorder _newMessageEntry;
...
_newMessageEntry = new EntrySetBorder
{
TextColor = Color.Black,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.End,
Margin = new Thickness(0, 0, 5, 0)
};
And:
EntrySetBorder entry = CurrentPage.FindByName<EntrySetBorder>("_newMessageEntry");
entry.Focus();
Or you go with this:
private EntrySetBorder _newMessageEntry;
...
_newMessageEntry = new EntrySetBorder
{
TextColor = Color.Black,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.End,
Margin = new Thickness(0, 0, 5, 0)
};
public EntrySetBorder NewMessageEntry => _newMessageEntry;
and :
EntrySetBorder entry = CurrentPage.FindByName<EntrySetBorder>("NewMessageEntry");
entry.Focus();
Please try that :)
Edit 2:
After reviewing your code, and testing it, the final way to fix it was by sending the Entry as a parameter in the command you're using, example:
Inside the page you're creating:
sendButton.CommandParameter = NewMessageEntry; // We're adding the Entry we want to focus as a command parameter.
And inside your PageModel and the command we want to use:
public Command SendCommand
{
get
{
return new Command<Entry>((obj) => //obj here means the parameters we're sending I.E: the entry we set it in the page.
{
//The code you want to execute
Entry entry = obj;
entry.Focus();
});
}
}
Note that I used Entry because I didn't have all the implementation of your custom entry.
This is an example of how I do, before this I used to do using MessagingCenter
in xaml , you need to give an x:Name to the obj you want to make focus.
<!-- PICKER's DEFINITION -->
<DatePicker
x:Name="Datepicker"
Date="{Binding SelectedDate, Mode=TwoWay}"
IsEnabled="true"
IsVisible="false">
</DatePicker>
then you have to make reference to that control in your command parameter on a button or for example in this case I use a toolbar item.
<!-- MENU TOOLBAR -->
<ContentPage.ToolbarItems>
<ToolbarItem
Command="{Binding ShowCalendarCommand}"
Icon="Calendar"
CommandParameter="{x:Reference Datepicker}" />
</ContentPage.ToolbarItems>
then in your vm command :
#region toolbar commands
public ICommand ShowCalendarCommand => new RelayCommand<Object>(ShowCalendar);
#endregion
private void ShowCalendar(Object obj)
{
var calendar = (DatePicker)obj;
calendar.Focus();
// MessagingCenter.Send(this, "Calendar");
}

WPF Binding Image Source

maybe stupid question, but I don't know anymore...
I have ViewModel class like this:
public class MainWindowsViewModel : INotifyPropertyChanged
{
private ImageSource _img;
public ImageSource StatusImage
{
get { return _img; }
set
{
_img = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Binding in XAML looks like this:
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
<Image x:Name="gui_image_status" HorizontalAlignment="Left" Height="26" Margin="144,10,0,0" VerticalAlignment="Top" Width="29" Source="{Binding Path=StatusImage}" />
And I set content of ImageSource like this:
MainWindowsViewModel _view = new MainWindowsViewModel();
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
_view.StatusImage = yourImage;
But it does not work. I think that problem is in that NotifyPropertyChanged, because I tried place brake point in the set and get. Get triggered few times at the start, after then set triggered as well with correct ImageSource, but after then get did not triggered anymore. Like no setting ever happened.
It's really simply binding that I have done many times similarly...I don't know why it doesn't work this time.
You are creating two instances of your MainWindowsViewModel class, one in XAML by
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
and one in code behind by
MainWindowsViewModel _view = new MainWindowsViewModel();
So your code behind sets the property on a different view model instance than the one the view is bound to.
Change your code behind to this:
var viewModel = (MainWindowsViewModel)DataContext;
viewModel.StatusImage = new BitmapImage(...);
I didn't find any problems in your code, but you can try to check few things.
Check that your Image added to the project and set build action of images to Content (copy if newer).
Before updating ImageSource call Freeze method to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
yourImage.Freeze();
_view.StatusImage = yourImage;
Also, there is an easier way to bind image in WPF. You can use string as a source and set a resource path to the binded property:
public string StatusImage
{
get { return "/AssemblyName;component/Sources/red.png"; }
}

Allowing named elements in a multi-content custom control

Requirement
Let's start with what I am trying to achieve. I want to have a grid with 2 columns and a grid splitter (there is a little more to it that that, but let's keep it simple). I want to be able to use this grid in a lot of different places, so instead of creating it each time I want to make a custom control that contain two ContentPresenters.
The end goal is effectively to be able to write XAML like this:
<MyControls:MyGrid>
<MyControls:MyGrid.Left>
<Label x:Name="MyLabel">Something unimportant</Label>
</MyControls:MyGrid.Left>
<MyControls:MyGrid.Right>
<Label>Whatever</Label>
</MyControls:MyGrid.Right>
</MyControls:MyGrid>
IMPORTANT: Notice that I want to apply a Name to my Label element.
Attempt 1
I did a lot of searching for solutions, and the best way I found was to create a UserControl along with a XAML file that defined my grid. This XAML file contained the 2 ContentPresenter elements, and with the magic of binding I was able to get something working which was great. However, the problem with that approach is not being able to Name the nested controls, which results in the following build error:
Cannot set Name attribute value 'MyName' on element 'MyGrid'. 'MyGrid'
is under the scope of element 'MyControls', which already had a name
registered when it was defined in another scope.
With that error in hand, I went back to Dr. Google...
Attempt 2 (current)
After a lot more searching I found some information here on SO that suggested the problem was due to having an associated XAML file with the MyGrid class, and the problem should be solvable by removing the XAML and creating all the controls via code in the OnInitialized method.
So I headed off down that path and got it all coded and compiling. The good news is that I can now add a Name to my nested Label control, the bad news is nothing renders! Not in design mode, and not when running the application. No errors are thrown either.
So, my question is: What am I missing? What am I doing wrong?
I am also open to suggestions for other ways to meet my requirements.
Current code
public class MyGrid : UserControl
{
public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Left
{
get { return (object)GetValue(LeftProperty); }
set { SetValue(LeftProperty, value); }
}
public static readonly DependencyProperty RightProperty = DependencyProperty.Register("Right", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Right
{
get { return (object)GetValue(RightProperty); }
set { SetValue(RightProperty, value); }
}
Grid MainGrid;
static MyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid)));
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
//Create control elements
MainGrid = new Grid();
//add column definitions
ColumnDefinition leftColumn = new ColumnDefinition()
{
Name = "LeftColumn",
Width = new GridLength(300)
};
MainGrid.ColumnDefinitions.Add(leftColumn);
MainGrid.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = GridLength.Auto
});
//add grids and splitter
Grid leftGrid = new Grid();
Grid.SetColumn(leftGrid, 0);
MainGrid.Children.Add(leftGrid);
GridSplitter splitter = new GridSplitter()
{
Name = "Splitter",
Width = 5,
BorderBrush = new SolidColorBrush(Color.FromArgb(255, 170, 170, 170)),
BorderThickness = new Thickness(1, 0, 1, 0)
};
MainGrid.Children.Add(splitter);
Grid rightGrid = new Grid();
Grid.SetColumn(rightGrid, 1);
MainGrid.Children.Add(rightGrid);
//add content presenters
ContentPresenter leftContent = new ContentPresenter();
leftContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Left") { Source = this });
leftGrid.Children.Add(leftContent);
ContentPresenter rightContent = new ContentPresenter();
rightContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Right") { Source = this });
rightGrid.Children.Add(rightContent);
//Set this content of this user control
this.Content = MainGrid;
}
}
After some discussion via comments, it quickly became clear that neither of my attempted solutions was the correct way to go about it. So I set out on a third adventure hoping this one would be the final solution... and it seems it is!
Disclaimer: I do not yet have enough experience with WPF to confidently say that my solution is the best and/or recommended way to do this, only that it definitely works.
First of all create a new custom control: "Add" > "New Item" > "Custom Control (WPF)". This will create a new class that inherits from Control.
In here we put our dependency properties for bind to out content presenters:
public class MyGrid : Control
{
public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Left
{
get { return (object)GetValue(LeftProperty); }
set { SetValue(LeftProperty, value); }
}
public static readonly DependencyProperty RightProperty = DependencyProperty.Register("Right", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Right
{
get { return (object)GetValue(RightProperty); }
set { SetValue(RightProperty, value); }
}
static MyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid)));
}
}
When you add this class file in Visual Studio, it will automatically create a new "Generic.xaml" file in the project containing a Style for this control, along with a Control Template within that style - this is where we define our control elements...
<Style TargetType="{x:Type MyControls:MyGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyControls:MyGrid}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<ContentPresenter x:Name="LeftContent" />
</Grid>
<GridSplitter Width="5" BorderBrush="#FFAAAAAA" BorderThickness="1,0,1,0">
</GridSplitter>
<Grid Grid.Column="1">
<ContentPresenter x:Name="RightContent" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The final step is to hook up the bindings for the 2 content presenters, so back to the class file.
Add the following override method to the MyGrid class:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//Apply bindings and events
ContentPresenter leftContent = GetTemplateChild("LeftContent") as ContentPresenter;
leftContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Left") { Source = this });
ContentPresenter rightContent = GetTemplateChild("RightContent") as ContentPresenter;
rightContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Right") { Source = this });
}
And that's it! The control can now be used in other XAML code like so:
<MyControls:MyGrid>
<MyControls:MyGrid.Left>
<Label x:Name="MyLabel">Something unimportant</Label>
</MyControls:MyGrid.Left>
<MyControls:MyGrid.Right>
<Label>Whatever</Label>
</MyControls:MyGrid.Right>
</MyControls:MyGrid>
Thanks to #NovitchiS for your input, your suggestions were vital in getting this approach to work

Categories