|
I have a textbox display that has incoming data from a serial port that I display. After 200 lines of data that has came in I want to remove 20 lines at the beginning. Here is my code:
private void TextBoxADCPDisplay_TextChanged(object sender, TextChangedEventArgs e)
{
TextBoxADCPDisplay.SelectionStart = TextBoxADCPDisplay.Text.Length;
TextBoxADCPDisplay.ScrollToEnd();
if (TextBoxADCPDisplay.LineCount > 200)
{
TextBoxADCPDisplay.Select(0, 20);
TextBoxADCPDisplay.SelectedText = "";
}
}
I get some strange behavior. Where after I hit 200 lines I see multiple lines that are the same where they should be different, but if I manually scroll up a little and scroll back down the lines of data that are coming in display as they should. What am I doing wrong?
|
|
|
|
|
geomeo123 wrote:
TextBoxADCPDisplay.Select(0, 20);
TextBoxADCPDisplay.SelectedText = ""; Well, start with the fact that the code you've written will not remove 20 lines; it will remove 20 characters.
Parameters
start The zero-based character index of the first character in the selection.
length The length of the selection, in characters.
So unless you can guarantee that every line is precisely 1 character long, and that the Select method ignores the line-break characters, your code isn't going to do what you want it to do.
If you want to remove 20 lines, you'll need to call TextBox.GetLineLength[^] for each line you want to remove, and sum up the line lengths to get the number of characters you need to select. NB: You'll need to test whether or not the returned value includes the line-break characters, as that's not clear from the documentation.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I had this code for my old windows form. At the time I didn't pay much attention to it as I had found it on a forum somewhere and I just copied and pasted. The original problem was in Windows forms. After a certain period of time of taking data in the entire program would crash. The code above resolved it, but moving over to WPF I'm finding a lot of things don't work as they should or even have the same methods. I am liking the fact that WPF is a lot more forgiving on certain things though so all good.
Thanks
|
|
|
|
|
I'm looking at the deault combobox style[^]
I'm not entirely sure how to ask what I'm thinking, so please bear with me...
Is it possible to replace a part of the control? For example, if I wanted to create a custome combobox. I know in the C# file I just subclass it
public class MyComboBox : ComboBox
{
static MyComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyComboBox),
new FrameworkPropertyMetadata(typeof(MyComboBox)));
}
}
But in the Generic.xaml, I seems like I would need to have ALL the default xaml from the site above - so that it looks & functions like a combobox.
Is there a way for me to provide just a new ContentSite? Can I justy define that in my Generic.xaml and somehow apply that to my combobox?
Ot do I need to copy in all the default XAML?
Thanks
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I seriously recommend picking up a book on WPF. This kind of stuff is covered in them.
Yes, you can replace just about anything you want in a WPF control. You just have to provide the implementation of what you want to replace, not the entire control or any of the implementation above what you're replacing.
The trick is knowing what in the XAML tree you need to replace, and where in the tree you need to provide it. Say you want to replace the Text property of a TextBox with a set of async priority bindings. You don't need to provide all the other XAML for a TextBox. You just need to specify the bindings:
<TextBox>
<TextBox.Text>
<PriorityBinding>
<Binding Path="SlowProperty" IsAsync="true" />
<Binding Path="MediumProperty" IsAsync="true" />
<Binding Path="FastProperty" />
</PriorityBinding>
</TextBox.Text>
</TextBox>
There isn't a XAML and C# component to a control. XAML is run through a compiler to generate the C# code. They are interchangeable, not complimentary to each other. The C# code for the control can exist entirely without XAML and the control will still work just fine. XAML is just a structured representation of a bunch of C# classes with properties being set.
Can you replace the ContentControl of a ComboBox? Sure. Can you replace the XAML and provide your own render code for the Combo? Again, sure.
Is it appropriate to do this? I have no idea what you're trying to do with the Combo, so I can't say.
It's also possible to build your own control that looks and acts like a Combo, but doesn't use a ComboBox, or is even based on the existing ComboBox, control at all. You can build one using a TextBox and a PopUp control to do weird stuff like a "ComboBox" that drops down and shows a bunch of TreeView controls in it.
modified 15-Jun-24 12:16pm.
|
|
|
|
|
I do WPF for a living. I have the Control Development book. In the part about creating custom controls, ALL the xaml from the template is used in the examples.
I want to create subclass of a combox box. I don't need the XAML for the button or the textbox, or any other part except for the content site. If you go look at the XAML in the link I provided, I want to replace this:
<ContentPresenter x:Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Stretch"
HorizontalAlignment="Left">
Something like this
public class MyComboBox : ComboBox
{
static MyComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyComboBox),
new FrameworkPropertyMetadata(typeof(MyComboBox)));
}
}
and
<Style TargetType="{x:Type ctrls:MyComboBox}">
<pre>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
</ControlTemplate>
</Setter.Value>
</Setter>
If I do this XAML, the ENTIRE template is replaced, so I'd have to recreate the textbox, button, etc.
What I'm asking is, instead of replacing the ENTIRE TEMPLATE, can I somehow replace just a small piece of that template?
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I have some code I'm trying to move to my view model. And it appears that my Invoke method does not work in it. I have the following code...
public delegate void SetTextCallBackSMT(string text);
Handing my serial port
string SMTData = SpSMT.ReadExisting();
if (SMTData.Contains("\r"))
{
modeSMT = ModeSMT.readLineSMTData;
}
else
{
Invoke(new SetTextCallBackSMT(SetTextSMTDisplay), SMTData.ToString());
}
And my set text method....
private void SetTextSMTDisplay(string text)
{
this.RichTextBoxSMTDisplay.Text += text;
}
The problem I'm having is that Invoke does not exist in the current context. I've tried adding forms name space. I've tried Dispatcher.Invoke, but I can't get that to work either. Any ideas?
|
|
|
|
|
Most code in the view-model doesn't need to worry about threading issues. The binding to the view will usually take care of that for you.
The only exception I'm aware of is if you're updating a collection from a background thread, where you may need to call the BindingOperations.EnableCollectionSynchronization[^] method to get it to work properly.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
This is extremely helpful to know! Thanks!
|
|
|
|
|
I'm trying to update my program to WPF MVVM with Icommands to make my program look a little better and reduce the amount of event handlers, clutter etc, which I've managed to make workish. Well if I press a button in WPF my message box executes, so that's always good right? But I have some properties in my old windows form load event that I need to move over and I'm not exactly sure how I would implement that. I'm using the serialport io namespace and so I'm loading things like this at form load time:
SP.Handshake = Handshake.None;
SP.DataReceived += new SerialDataReceivedEventHandler(SP_DataReceived);
SP meaning serial port. Do I put the above in my wpf form load event and make these public so that my modelview class can see them? Or is there an alternative? My model view class inherits from my viewbasemodel so it doesn't allow inheriting from another base class. I could move them to my viewbasemodel class, but I'm not sure about that one either. I still have to make them run at form load. I have to say I'm only half understanding the modelview at the moment. The tutorials online aren't clicking just yet. But I have made it workish so that's always good.
|
|
|
|
|
geomeo123 wrote: Do I put the above in my wpf form load event and make these public so that my modelview class can see them?
No. The ViewModel should not have any reference to the view.
The quick fix would be to move the serial port comms into the ViewModel.
A better option would probably be to move it to a separate "service" class consumed by the ViewModel, particularly if you want to introduce unit tests for your ViewModel classes.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Ok so I put them in ViewModel as a quick fix for now, but how would I call that from my xaml form? The xaml loaded event doesn't allow me to bind to my static resource(thinking I could just do another Icommand). Maybe because my static resource is called after I'm putting in loaded in xaml, but I can't think of any other way.
|
|
|
|
|
The typical solution would be to use an "attached behaviour" to bind the Loaded event to your command.
Introduction to Attached Behaviors in WPF[^]
For example:
public static class LoadedBehavior
{
public static readonly DependencyProperty LoadedCommandProperty = DependencyProperty.RegisterAttached(
"LoadedCommand",
typeof(ICommand),
typeof(LoadedBehavior),
new PropertyMetadata(null, OnLoadedCommandChanged));
public static ICommand GetLoadedCommand(FrameworkElement obj)
{
return (ICommand)obj.GetValue(LoadedCommandProperty);
}
public static void SetLoadedCommand(FrameworkElement obj, ICommand value)
{
obj.SetValue(LoadedCommandProperty, value);
}
private static void OnLoadedCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = obj as FrameworkElement;
if (element == null) return;
if (e.OldValue == null)
{
element.Loaded += OnLoaded;
}
else if (e.NewValue == null)
{
element.Loaded -= OnLoaded;
}
}
public static readonly DependencyProperty LoadedCommandParameterProperty = DependencyProperty.RegisterAttached(
"LoadedCommandParameter",
typeof(object),
typeof(LoadedBehavior),
new PropertyMetadata(null));
public static object GetLoadedCommandParameter(FrameworkElement obj)
{
return obj.GetValue(LoadedCommandParameterProperty);
}
public static void SetLoadedCommandParameter(FrameworkElement obj, object value)
{
obj.SetValue(LoadedCommandParameterProperty, value);
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
if (element == null) return;
ICommand command = GetLoadedCommand(element);
if (command == null) return;
object parameter = GetLoadedCommandParameter(element);
command.Execute(parameter);
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
|
Thank you much Richard. I think I got it. Thanks again!
|
|
|
|
|
I'm trying to use a WrapPanel inside an ItemsControl. I'm following this[^].
The difference is that mine is bound to a list of objects called Backups.
Here's what I have. When I run it, I don't see any of the data.
<Border Grid.Row="1"
CornerRadius="20"
Margin="35"
Padding="10"
Background="Tan"
BorderThickness="3"
BorderBrush="SteelBlue">
<ScrollViewer VerticalScrollBarVisibility="Auto"
Background="Teal"
Margin="10">
<ItemsControl ItemsSource="{Binding Backups}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Salmon"
BorderBrush="Blue"
BorderThickness="3">
<StackPanel Orientation="Vertical">
<Rectangle Margin="5"
Width="100"
Height="100"
Fill="Yellow"
HorizontalAlignment="Center"/>
<TextBlock Text="THIS IS A TEST"
HorizontalAlignment="Center"
Margin="5"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Border>
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
modified 29-May-24 22:23pm.
|
|
|
|
|
If you remove the ItemsPanel , do you see the items?
If not, are there any binding errors logged in the Visual Studio output window?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I removed the ItemsPanel, and there are no binding errors, yet I still see nothing.
Also, I just created a quick test app doing the same thing, and it works:
GitHub - MaroisConsulting/Marois.WrapPanelDemo
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
Kevin Marois wrote: I removed the ItemsPanel, and there are no binding errors, yet I still see nothing.
Which means the problem is nothing to do with the items panel; either it's not binding to the collection, or the collection is empty.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I am facing an exception while WebView initialize. It is working perfectly fine when i debug my code on VS 2019 but when i make an installer and install it on VM Windows 10 Machine it gives me an exception which i can get with help of Logs.
I have added UDF as well in my client machine and install Microsoft edge runtime(124.0.2478.97)
Please Suggest any one what i need to add to resolve this issue.
Code Block
MainWindow.xaml.cs
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
public MainWindow()
{
InitializeComponent();
InitializeAndNavigateAsync( url);
}
public async Task InitializeAndNavigateAsync(string url)
{
await InitializeWebViewAsync();
await NavigateToUrlAsync(url);
}
private async Task InitializeWebViewAsync()
{
try
{
log.Debug($"WebView2 initialize start");
await webView.EnsureCoreWebView2Async(null);
log.Debug($"WebView2 initialize stop");
}
catch (Exception e)
{
log.Debug($"WebView2 initialization failed.{e.Message}");
log.Debug($"WebView2 initialization failed.{e.StackTrace}");
}
}
private async Task NavigateToUrlAsync(string url)
{
try
{
log.Debug($"WebView2 NavigateToUrlAsync start");
webView.CoreWebView2.Navigate(url);
log.Debug($"WebView2 NavigateToUrlAsync stop");
}
catch (Exception e)
{
log.Debug($"WebView2 NavigateToUrlAsync failed.{e.Message}");
log.Debug($"WebView2 NavigateToUrlAsync failed.{e.StackTrace}");
}
}
Exception is :
WebView2 initialization failed.Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE))
2024-05-14 06:34:45.4923|DEBUG|debug|WebView2 initialization failed. at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at Microsoft.Web.WebView2.Core.CoreWebView2Environment.<CreateCoreWebView2ControllerAsync>d__80.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Web.WebView2.Wpf.WebView2.<>c__DisplayClass32_0.<<EnsureCoreWebView2Async>g__Init|0>d.MoveNext()
2024-05-14 06:34:45.4923|DEBUG|debug|WebView2 initialization failed.mscorlib
|
|
|
|
|
In your InitializeWebViewAsync method, try creating a CoreWebView2Environment object (you'll need to give it a folder path to use) and passing that to the EnsureCoreWebView2Async method.
|
|
|
|
|
I have a standard C#, WPF, and PRISM project, and I wanted to have one module per tab item. But I want to bind the header of a property in the ViewModel directly to the parent TabItem.
I tried this (among other things such as Content.DataContext.Message etc.) :
<TabItem Header="{Binding DataContext.Message, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}"></ContentControl>
</TabItem>
I have a distinct feeling I have done this before, but I cannot figure it out at the moment, so any suggestions?
|
|
|
|
|
I'm building and populating a DataGrid in a viewmodel.
Here's what I'm making[^]
The two columns on the right, AutoAdjustment and AutoPosting, are combo boxes where the user can selecte 'Y' or 'N'. These are the only editable columns.
For my source data, I have a list of objects called ProcoessingOutputEntries, each with a list of column objects. It's basically a 'row' object with a dynamic collection of columns on it:
private List<ProcessingOutputEntryEntity> _ProcesssingOutputEntries;
public List<ProcessingOutputEntryEntity> ProcesssingOutputEntries
{
get { return _ProcesssingOutputEntries; }
set
{
if (_ProcesssingOutputEntries != value)
{
_ProcesssingOutputEntries = value;
RaisePropertyChanged(nameof(ProcesssingOutputEntries));
}
}
}
and
public class ProcessingOutputEntryEntity : _EntityBase
{
private List<ColumnInfoEntity> _ProcesssingOutputColumns;
public List<ColumnInfoEntity> ProcesssingOutputColumns
{
get { return _ProcesssingOutputColumns; }
set
{
if (_ProcesssingOutputColumns != value)
{
_ProcesssingOutputColumns = value;
RaisePropertyChanged(nameof(ProcesssingOutputColumns));
}
}
}
}
and
public class ColumnInfoEntity : _EntityBase
{
private string _ColumnName;
public string ColumnName
{
get { return _ColumnName; }
set
{
if (_ColumnName != value)
{
_ColumnName = value;
RaisePropertyChanged(nameof(ColumnName));
}
}
}
private ColumnSources _ColumnSource;
public ColumnSources ColumnSource
{
get { return _ColumnSource; }
set
{
if (_ColumnSource != value)
{
_ColumnSource = value;
RaisePropertyChanged(nameof(ColumnSource));
}
}
}
private ColumnTypes _ColumnType;
public ColumnTypes ColumnType
{
get { return _ColumnType; }
set
{
if (_ColumnType != value)
{
_ColumnType = value;
RaisePropertyChanged(nameof(ColumnType));
}
}
}
private int _ColumnWidth;
public int ColumnWidth
{
get { return _ColumnWidth; }
set
{
if (_ColumnWidth != value)
{
_ColumnWidth = value;
RaisePropertyChanged(nameof(ColumnWidth));
}
}
}
private string _DisplayAs;
public string DisplayAs
{
get { return _DisplayAs; }
set
{
if (_DisplayAs != value)
{
_DisplayAs = value;
RaisePropertyChanged(nameof(DisplayAs));
}
}
}
private int _IndexPosition;
public int IndexPosition
{
get { return _IndexPosition; }
set
{
if (_IndexPosition != value)
{
_IndexPosition = value;
RaisePropertyChanged(nameof(IndexPosition));
}
}
}
private bool _IsUserEditable = true;
public bool IsUserEditable
{
get { return _IsUserEditable; }
set
{
if (_IsUserEditable != value)
{
_IsUserEditable = value;
RaisePropertyChanged(nameof(IsUserEditable));
}
}
}
private object _Data;
public object Data
{
get { return _Data; }
set
{
if (_Data != value)
{
_Data = value;
RaisePropertyChanged(nameof(Data));
}
}
}
public override string ToString()
{
return $"{ColumnSource} {IndexPosition} {ColumnName}";
}
}
I handle the WindowLoaded event to create the grid in my ViewModel:
private void WindowLoadedExecuted()
{
_results.ProcesssingOutputEntries.ForEach(x => x.Id = Guid.NewGuid());
foreach (var entry in _results.ProcesssingOutputEntries)
{
var col = new ColumnInfoEntity
{
ColumnName = "Id",
Data = entry.Id.ToString()
};
entry.ProcesssingOutputColumns.Insert(0, col);
}
DataColumn column = null;
var firstEntry = _results.ProcesssingOutputEntries.FirstOrDefault();
foreach (var col in firstEntry.ProcesssingOutputColumns)
{
column = new DataColumn
{
DataType = Type.GetType("System.String"),
ReadOnly = false
};
ColumnsDataTable.Columns.Add(column);
}
var autoAdjColIndex = -1;
var autoPostColIndex = -1;
var autoAdjCol = GetProcessingOutputColumn(ColumnTypes.AutoAdjustment);
if(autoAdjCol != null)
{
autoAdjColIndex = firstEntry.ProcesssingOutputColumns.IndexOf(autoAdjCol);
}
var autoPostCol = GetProcessingOutputColumn(ColumnTypes.AutoPosting);
if (autoPostCol != null)
{
autoPostColIndex = firstEntry.ProcesssingOutputColumns.IndexOf(autoPostCol);
}
string[] columnNames = (from dc in ColumnsDataTable.Columns.Cast()
select dc.ColumnName).ToArray();
var textBoxStyle = new Style();
textBoxStyle.Setters.Add(new Setter(TextBox.TextAlignmentProperty, TextAlignment.Right));
var comboBoxStyle = new Style();
comboBoxStyle.Setters.Add(new EventSetter(ComboBox.SelectionChangedEvent, new SelectionChangedEventHandler(ComboSelectionChanged)));
int index = 0;
foreach (string columnName in columnNames)
{
var binding = new Binding(columnName);
if (index == autoAdjColIndex || index == autoPostColIndex)
{
var col = new DataGridComboBoxColumn()
{
Header = firstEntry.ProcesssingOutputColumns[index].ColumnName,
ItemsSource = new List { "Y", "N" },
Visibility = Visibility.Visible,
TextBinding = binding,
IsReadOnly = false,
CellStyle = comboBoxStyle,<br />
};
DataGridColumns.Add(col);
}
else
{
var col = new DataGridTextColumn()
{
Header = firstEntry.ProcesssingOutputColumns[index].ColumnName,
Binding = binding,
Visibility = Visibility.Visible,
IsReadOnly = true,
CellStyle = textBoxStyle
};
DataGridColumns.Add(col);
}
index++;
}
LoadData();
}
Here's how the data is loaded
private void LoadData()
{
ColumnsDataTable.Clear();
foreach (var processingEntry in _results.ProcesssingOutputEntries)
{
DataRow row = ColumnsDataTable.NewRow();
ColumnsDataTable.Rows.Add(row);
var index = 0;
foreach (var col in processingEntry.ProcesssingOutputColumns)
{
row[index] = col.Data;
index++;
}
}
}
The ComboBox column style handles the SelectionChanged event:
var comboBoxStyle = new Style();
comboBoxStyle.Setters.Add(new EventSetter(ComboBox.SelectionChangedEvent, new SelectionChangedEventHandler(ComboSelectionChanged)));
and
private void ComboSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = e.Source as ComboBox;
var dataGrid = WPFTools.GetVisualParent(comboBox);
var dataRowView = dataGrid.SelectedItem as DataRowView;
if(dataRowView != null)
{
var newSelection = e.AddedItems[0];
var rowId = dataRowView.Row[0].ToString();
}
}
So, the question is, in this event handler, how do I set the newly selected value back to the source row? It seems like I need to get the row, then the bound object from it. But how do I know which combox fired this event?
Thanks
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
modified 8-Apr-24 20:10pm.
|
|
|
|
|
Probably an unwanted comment but why not use a checkbox binding directly to the datasource, less clicks for the user, and no faffing around with combobox and dodgy binding. You could even add the Yes/No based on the checked state.
Never underestimate the power of human stupidity -
RAH
I'm old. I know stuff - JSOP
|
|
|
|
|
I'm sure this is a silly question....
I'm trying to port some behaviors from .Net Framewor 4.7 to Core 6.
This no longer compiles:
public class ComboBoxWatermarkBehavior : Behavior<ComboBox>
.
.
.
because it can't find Behavior<>
From what I can see from Google results, I was missing Microsoft.Xaml.Behaviors.Wpf. I added the package but it didn't do anything.
What am I missing here???
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|