本文记录了笔者在阅读 WPF 程序过程中不懂的知识点和一些心得、理解,同时也是一种高效的学习方式的探索。
40. public ObservableCollection<string> Labels { get; set; } = new ObservableCollection<string>();
ObservableCollection<T>
是 .NET Framework 提供的一个泛型集合类,它继承自Collection<T>
并实现了INotifyPropertyChanged
和INotifyCollectionChanged
接口。这使得ObservableCollection<T>
能够在添加、删除或替换项时自动通知绑定机制(如 WPF 数据绑定),从而更新用户界面。- 在这个场景中,
ObservableCollection<string>
用于存储string
对象的集合,并且当集合发生变化时,能够自动通知任何监听这些变化的部分(例如,UI 元素)。
-
访问修饰符(Access Modifier) :
public
: 这意味着Labels
属性可以在类的外部被访问和修改。它是公开的,允许其他类或对象直接获取或设置其值。
-
属性类型(Property Type) :
ObservableCollection<string>
:Labels
属性是一个ObservableCollection<T>
类型的集合,其中T
被指定为string
。ObservableCollection<T>
是一个动态数据集合,它提供了在添加、删除或更改集合中的项时能够自动通知的机制。这对于数据绑定场景特别有用,比如 WPF、Xamarin 或其他支持 INotifyPropertyChanged 和 INotifyCollectionChanged 接口的 .NET 应用程序。
-
自动实现的属性(Auto-Implemented Property) :
{ get; set; }
: 这表明Labels
是一个自动实现的属性,它背后有一个由编译器自动生成的私有字段来存储数据。get
访问器用于返回属性的值,而set
访问器用于设置属性的值。
-
属性初始化(Property Initialization) :
= new ObservableCollection<string>()
: 这部分代码在属性声明时立即初始化了一个新的ObservableCollection<string>
实例,并将其赋值给Labels
属性。这意味着,当创建包含Labels
属性的类的实例时,Labels
属性将自动包含一个空的字符串集合,准备好用于添加项。
综上所述,代码 public ObservableCollection<string> Labels { get; set; } = new ObservableCollection<string>();
定义了一个公开的、可自动通知变化的字符串集合属性 Labels
。这个属性在类的实例被创建时会被初始化为一个空的 ObservableCollection<string>
实例。
这样的属性通常用于需要动态更新 UI 以反映数据集合变化的场景,特别是在数据绑定到 UI 元素(如 WPF 的 ListBox、ComboBox 或其他控件)时。当向 Labels
集合中添加、删除或更改项时,绑定到该集合的 UI 元素会自动更新以反映这些变化。
41. List<T>
在C#编程语言中,List<T>
是一种极为关键且常用的泛型集合类,它隶属于System.Collections.Generic
命名空间。List<T>
为开发者提供了一个能够按需动态调整大小的数组实现,即它支持在运行时进行元素的添加、移除以及访问等操作。以下是对List<T>
数据结构的详细解读,并通过代码示例加以说明:
命名空间引入
首先,若想在代码中使用List<T>
,需引入其所在的命名空间:
csharp复制代码
using System.Collections.Generic;
List<T>
的声明与初始化
List<T>
的声明需明确元素类型T
,例如,若需存储整数,则应声明为List<int>
;若存储字符串,则声明为List<string>
。以下是一个存储字符串的List<T>
声明与初始化示例:
List<string> myList = new List<string>();
此外,List<T>
还支持在声明时直接进行初始化,通过大括号{}
内嵌一组初始元素:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
List<T>
的常用操作
- 添加元素:
利用Add
方法可向List<T>
中添加新元素。
myList.Add("Hello");
myList.Add("World");
- 访问元素:
可通过索引器直接访问List<T>
中的元素,索引从0起始。
string firstElement = myList[0]; // 获取首个元素
- 移除元素:
Remove
方法可移除首个匹配的元素,而RemoveAt
则根据索引移除元素。
myList.Remove("Hello"); // 移除元素"Hello"
myList.RemoveAt(0); // 移除索引为0的元素(若存在)
- 遍历元素:
foreach
循环是遍历List<T>
的推荐方式。
foreach (string item in myList)
{
Console.WriteLine(item);
}
- 获取元素数量:
Count
属性返回List<T>
中元素的数量。
int count = myList.Count; // 获取元素总数
- 清空列表:
Clear
方法将移除List<T>
中的所有元素。
myList.Clear(); // 清空列表
- 查找元素索引:
IndexOf
方法返回指定元素的索引,若元素不存在则返回-1。
int index = myList.IndexOf("World"); // 查找"World"的索引
- 是否存在元素:
Contains
方法检查List<T>
中是否包含特定元素。
bool containsHello = myList.Contains("Hello"); // 检查是否包含"Hello"
示例代码整合
以下是一个整合了上述操作的完整示例:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 初始化List<string>并添加元素
List<string> greetings = new List<string> { "Hello", "World", "!" };
// 访问并打印首个元素
Console.WriteLine(greetings[0]);
// 添加新元素
greetings.Add("Welcome");
// 遍历并打印所有元素
foreach (string greeting in greetings)
{
Console.WriteLine(greeting);
}
// 移除元素并检查是否包含特定元素
greetings.Remove("World");
bool hasExclamation = greetings.Contains("!");
Console.WriteLine("Contains '!': " + hasExclamation);
// 打印元素数量并清空列表
Console.WriteLine("Count: " + greetings.Count);
greetings.Clear();
Console.WriteLine("Count after Clear: " + greetings.Count);
}
}
上述示例详细展示了List<T>
的基本操作,包括初始化、添加、访问、移除、遍历、检查存在性以及清空列表等。
42. 当前时间 DateTime.Now.ToString("HH时mm分ss秒fff")
43. 去掉 string 第一个字母 name.StartsWith("_") ? name.Substring(1) : name;
44. 封装一个 post 请求方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RestSharp;
using RestSharp.Authenticators;
namespace VisionInspection.Models.Instance
{
public class HikLinkTask
{
private String username = "";
private String msg = "";
public void setConfig(string username, string msg)
{
this.username = username;
this.msg = msg;
}
public bool SendTask()
{
bool Result = false;
JObject jObject = new JObject
{
{ "userCode", username },
{ "msg", msg }
};
string strJson = JsonConvert.SerializeObject(jObject);
string result = Post("http://*****", strJson);
Result = result == "OK" ? true:false;
return Result;
}
public string Post(string Url, string jsonParas)
{
var restClient = new RestClient(Url);
var restRequest = new RestRequest(Method.POST);
restRequest.AddHeader("token", "notoken");
restRequest.AddParameter("application/json", jsonParas, ParameterType.RequestBody);
//restRequest.AddParameter("JSON", jsonParas);
var iRestResponse = restClient.Execute(restRequest);
if (iRestResponse.StatusCode != HttpStatusCode.OK)
{
return iRestResponse.Content;
}
else
{
return iRestResponse.StatusCode.ToString();
}
}
}
}
45. Dictionary<T, P>
定义和使用
static Dictionary<int, string> errorDic = new Dictionary<int, string>() { { 0,"没有错误。" } }
这段代码定义了一个静态的Dictionary<int, string>
类型的变量,名为errorDic
。这个字典用于存储键值对,其中键是整数类型(int
),而值是字符串类型(string
)。在这个特定的例子中,字典被初始化为只包含一个键值对。
详细解释如下:
static
:这个关键字表示errorDic
是一个静态变量。静态变量属于类本身,而不是类的任何特定实例。因此,你可以在不创建类的实例的情况下访问它,并且所有类的实例共享同一个静态变量的值。Dictionary<int, string>
:这指定了变量的类型。Dictionary
是一个泛型集合,它存储键值对。在这个例子中,键是int
类型,而值是string
类型。这意味着你可以使用整数作为键来存储和检索字符串值。errorDic
:这是变量的名称。new Dictionary<int, string>()
:这创建了Dictionary<int, string>
类型的一个新实例。{}
:这对大括号内包含了字典的初始化列表。在这个列表中,你可以指定一个或多个键值对来初始化字典。{0, "没有错误。"}
:这是初始化列表中的第一个(也是唯一一个)键值对。键是整数0
,而与之关联的值是字符串"没有错误。"
。
因此,这段代码创建了一个静态字典errorDic
,它有一个条目,表示错误代码0
对应的错误消息是"没有错误。"
。这个字典可以用于存储和检索错误代码及其对应的错误消息。例如,如果某个函数返回一个错误代码,你可以使用这个字典来查找并显示相应的错误消息给用户。
要添加更多的错误代码和消息到字典中,你可以在初始化列表中继续添加键值对,如下所示:
static Dictionary<int, string> errorDic = new Dictionary<int, string>()
{
{0, "没有错误。"},
{1, "错误1的描述。"},
{2, "错误2的描述。"}
// ... 添加更多错误代码和消息
};
要使用C#代码从字典中获取信息,你可以通过字典的键(key)来检索对应的值(value)。以下是一个简单的例子,展示了如何从errorDic
字典中获取并打印错误代码为0的错误消息:
using System;
using System.Collections.Generic;
class Program
{
// 静态字典,用于存储错误代码和对应的错误消息
static Dictionary<int, string> errorDic = new Dictionary<int, string>()
{
{0, "没有错误。"},
{1, "发生了错误1。"},
{2, "发生了错误2。"}
// 可以继续添加更多错误代码和消息
};
static void Main(string[] args)
{
// 假设我们有一个错误代码
int errorCode = 0;
// 检查字典中是否包含该错误代码
if (errorDic.ContainsKey(errorCode))
{
// 从字典中获取对应的错误消息
string errorMessage = errorDic[errorCode];
// 打印错误消息
Console.WriteLine($"错误代码 {errorCode}: {errorMessage}");
}
else
{
// 如果字典中不包含该错误代码,打印未知错误消息
Console.WriteLine($"未知错误代码 {errorCode}。");
}
// 你也可以尝试获取其他错误代码的错误消息
errorCode = 1; // 更改错误代码以测试其他情况
if (errorDic.ContainsKey(errorCode))
{
string errorMessage = errorDic[errorCode];
Console.WriteLine($"错误代码 {errorCode}: {errorMessage}");
}
}
}
在这个例子中,我们首先定义了一个静态字典errorDic
,它包含了几个错误代码和对应的错误消息。在Main
方法中,我们有一个变量errorCode
,它模拟了一个从某处(比如函数返回值)获取的错误代码。我们使用ContainsKey
方法来检查字典中是否包含该错误代码,如果包含,则使用索引器[]
从字典中获取对应的错误消息,并将其打印到控制台。如果字典中不包含该错误代码,我们打印一个未知错误消息。
你可以根据需要更改errorCode
的值来测试不同的错误代码和对应的错误消息。
46. JSON 辅助函数
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VisionInspection.Models.ClassFiles
{
public class JsonHelper
{
public static string GetNestedPropertyValue(JObject json, string path)
{
var properties = path.Split('.');
var current = json;
foreach (var property in properties)
{
if (current[property] is JObject child)
{
current = child;
}
else if (current[property] is JValue value)
{
return value.ToString();
}
else if (current[property]!=null)
{
return current[property].ToString();
}
else
{
return null;
}
}
return null;
}
}
}
这段代码定义了一个名为JsonHelper
的静态类,其中包含一个名为GetNestedPropertyValue
的静态方法。这个方法的目的是从一个JObject
(代表一个JSON对象)中提取嵌套属性的值。方法接受两个参数:一个是JObject
类型的json
,代表要查询的JSON对象;另一个是string
类型的path
,代表要查询的嵌套属性的路径,路径中的各个属性名通过点(.
)分隔。
方法的实现逻辑如下:
-
使用
Split('.')
方法将路径字符串分割成一个属性名数组。 -
初始化一个变量
current
,将其设置为传入的json
对象。 -
遍历属性名数组,对于数组中的每个属性名:
- 如果
current
对象中包含该属性,并且该属性的值是一个JObject
(即另一个嵌套对象),则将current
更新为该嵌套对象。 - 如果
current
对象中包含该属性,并且该属性的值是一个JValue
(即一个具体的值,如字符串、数字等),则返回该值的字符串表示。 - 如果
current
对象中包含该属性,但该属性的值既不是JObject
也不是JValue
(这实际上不太可能,因为JSON对象的属性值通常只能是这两种类型之一),则返回该属性的值的字符串表示(这里的处理有些冗余,因为前面的条件已经涵盖了所有可能的情况,这个else分支理论上不会被执行到)。 - 如果
current
对象中不包含该属性,则返回null
。
- 如果
-
如果遍历完属性名数组后没有找到值(这通常不应该发生,除非路径字符串有误或JSON结构不符合预期),则返回
null
。
然而,代码中存在一些可以改进的地方:
- 最后一个
else
分支(检查current[property]!=null
)实际上是多余的,因为前面的条件已经处理了所有可能的情况。如果current
中不包含该属性,current[property]
将会是JToken.Load(JToken.Null)
,它既不是JObject
也不是JValue
,但由于JToken
是非空的(它表示一个空值,而不是一个不存在的属性),所以这个条件会错误地进入并返回一个空值的字符串表示(即空字符串),而不是null
。正确的做法是在找不到属性时直接返回null
,不需要这个额外的检查。 - 方法名
GetNestedPropertyValue
可能不够明确,因为它实际上返回的是字符串类型的值。如果将来需要返回其他类型的值,可能需要考虑更一般的命名或方法重载。
下面是一个使用JsonHelper.GetNestedPropertyValue
方法的例子:
using Newtonsoft.Json.Linq;
using System;
class Program
{
static void Main(string[] args)
{
string jsonString = @"{
'name': 'John Doe',
'address': {
'street': '123 Main St',
'city': 'Anytown'
},
'phoneNumbers': [
{
'type': 'home',
'number': '555-1234'
},
{
'type': 'work',
'number': '555-5678'
}
]
}";
JObject jsonObject = JObject.Parse(jsonString);
string name = JsonHelper.GetNestedPropertyValue(jsonObject, "name");
Console.WriteLine($"Name: {name}");
string city = JsonHelper.GetNestedPropertyValue(jsonObject, "address.city");
Console.WriteLine($"City: {city}");
// 注意:下面的代码将返回null,因为phoneNumbers是一个数组,不是直接嵌套的对象
string homePhoneNumber = JsonHelper.GetNestedPropertyValue(jsonObject, "phoneNumbers.0.number");
// 为了正确处理数组索引,我们需要稍微修改GetNestedPropertyValue方法
// ...(这里省略了修改方法的代码)
// 假设我们已经修改了方法以支持数组索引,那么下面的代码将正确工作
Console.WriteLine($"Home Phone Number: {homePhoneNumber}");
}
}
请注意,上面的代码尝试获取phoneNumbers
数组中的第一个电话号码,但原始的GetNestedPropertyValue
方法不支持数组索引。为了正确处理这种情况,我们需要对GetNestedPropertyValue
方法进行一些修改,以便它能够解析路径中的数组索引。然而,这超出了当前问题的范围,并且可能需要根据具体需求进行不同的实现。
47. BitmapImage bitmapImage = new BitmapImage();
这行代码在C#中执行了几个关键操作:
- 类型声明:
BitmapImage
是声明的变量类型。BitmapImage
类通常用于WPF(Windows Presentation Foundation)应用程序中表示位图图像。它提供了加载、保存和操作图像数据的方法。 - 变量命名:
bitmapImage
是变量的名称。通过这个名称,你可以在代码的其他部分引用这个特定的BitmapImage
实例。 - 实例化:
new BitmapImage()
创建了BitmapImage
类的一个新实例。new
关键字用于在C#中创建对象的实例。括号()
表示调用了BitmapImage
类的构造函数,这里是无参数的构造函数,它创建了一个默认的BitmapImage
对象。 - 赋值:
=
符号将新创建的BitmapImage
实例赋值给名为bitmapImage
的变量。
总结来说,这行代码创建了一个 BitmapImage
类型的对象,并将其引用存储在名为 bitmapImage
的变量中。之后,你可以使用这个变量来配置图像的源(例如,从一个文件或URI加载图像),或者将其用于WPF界面中的图像控件。
例如,要设置图像的源为一个URI,你可能会这样做:
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri("path/to/your/image.jpg", UriKind.RelativeOrAbsolute); // 根据你的图像路径调整
bitmapImage.EndInit();
然后,你可以将这个 bitmapImage
赋值给一个 Image
控件的 Source
属性,以便在WPF应用程序中显示图像。
48. bitmapImage.BeginInit(); ... bitmapImage.EndInit();
在WPF(Windows Presentation Foundation)中,BitmapImage
类用于表示位图图像,并且它经常与图像控件(如 Image
)一起使用来在应用程序中显示图像。BitmapImage
类的一些属性,如 UriSource
、DecodePixelWidth
、DecodePixelHeight
等,是可以影响图像加载和显示方式的重要属性。
当设置这些属性时,WPF 设计了一个初始化模式,要求在使用这些属性之前调用 BeginInit
方法,并在所有属性设置完成后调用 EndInit
方法。这种模式称为延迟初始化或逐步初始化。
代码简短的解释如下:
-
bitmapImage.BeginInit();
这行代码标记了初始化过程的开始。在调用
BeginInit
之后,你可以安全地设置BitmapImage
实例的多个属性,而不需要担心属性更改通知或性能开销,因为实际的初始化过程被延迟了。 -
...
这里省略了设置
BitmapImage
属性的代码。例如,你可能会设置UriSource
属性来指定图像的来源 URI,或者设置其他属性来调整图像的解码方式、缓存选项等。 -
bitmapImage.EndInit();
这行代码标记了初始化过程的结束。在调用
EndInit
之后,BitmapImage
实例会根据之前设置的属性进行实际的初始化工作,如加载图像数据、解码图像等。EndInit
方法还会触发任何必要的属性更改通知,并准备BitmapImage
实例以供使用。
重要的是要注意,在调用 EndInit
之后,再尝试设置 BitmapImage
的属性可能会导致异常或不可预测的行为,因为初始化过程已经完成,属性可能已经被锁定或应用于图像数据。
因此,在使用 BitmapImage
时,遵循 BeginInit
-> 设置属性 -> EndInit
的模式是非常重要的。
49. bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
在WPF(Windows Presentation Foundation)中,BitmapImage
类提供了一个 CacheOption
属性,该属性用于指定图像的缓存选项。BitmapCacheOption
是一个枚举类型,它决定了图像数据在何时被缓存到内存中。
代码解释如下:
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
这行代码将 BitmapImage
实例(bitmapImage
)的 CacheOption
属性设置为 BitmapCacheOption.OnLoad
。
bitmapImage
:这是已经创建并可能已配置其他属性的BitmapImage
实例的变量名。CacheOption
:这是BitmapImage
类的一个属性,它决定了图像数据的缓存策略。BitmapCacheOption.OnLoad
:这是BitmapCacheOption
枚举的一个值,它指示图像数据在图像加载到BitmapImage
实例时立即被缓存到内存中。这意味着一旦图像加载完成,它的数据就会被保留在内存中,直到应用程序或图像控件不再需要它为止。这可以提高图像在应用程序中的显示性能,特别是当图像需要多次显示或在不同部分重复使用时。
将 CacheOption
设置为 BitmapCacheOption.OnLoad
适用于那些预期会在应用程序生命周期中多次使用且不会频繁更改的图像。然而,对于大型图像或那些只使用一次的图像,可能会选择其他缓存选项(如 BitmapCacheOption.None
),以避免不必要的内存使用。
50. public Brush ShowColor { get; set; } = new SolidColorBrush(Colors.DarkGreen);
在C#中,这行代码定义了一个名为ShowColor
的属性,它是一个Brush
类型的对象。Brush
是WPF(Windows Presentation Foundation)中用于绘制图形和文本的对象的基础类型。这行代码还展示了属性初始化的语法,即直接在属性声明时为其赋予一个初始值。
代码解释如下:
public Brush ShowColor { get; set; } = new SolidColorBrush(Colors.DarkGreen);
public
:这是一个访问修饰符,表示ShowColor
属性是公开的,即它可以在类的外部被访问和修改。Brush
:这是属性的类型,表示ShowColor
可以存储任何Brush
类型的对象。Brush
是一个抽象基类,提供了用于绘制图形和文本的基本功能。ShowColor
:这是属性的名称。{ get; set; }
:这是自动实现的属性访问器。get
访问器用于返回属性的值,而set
访问器用于设置属性的值。在这个例子中,由于使用了自动实现属性,我们不需要显式地定义一个存储字段来保存属性的值,编译器会自动为我们处理这些细节。= new SolidColorBrush(Colors.DarkGreen);
:这是属性初始化器。它直接在属性声明时为其赋予了一个初始值,即一个新的SolidColorBrush
对象,该对象的颜色被设置为Colors.DarkGreen
。SolidColorBrush
是Brush
的一个具体实现,用于表示具有单一颜色的画笔。Colors.DarkGreen
是一个预定义的颜色值,表示深绿色。
总结来说,这行代码定义了一个公开的Brush
类型属性ShowColor
,并将其初始化为一个深绿色的SolidColorBrush
对象。在WPF应用程序中,这样的属性通常用于数据绑定,以动态地改变UI元素的外观,例如改变按钮的背景色或文本的颜色。
51. 文件通过 FTP 服务传送
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace TemplateProject.ClassFiles
{
class FTPHelper
{
/// <summary>
/// FTP的服务器地址,格式为ftp://192.168.1.234:8021/。
/// </summary>
private string FTPCONSTR { get; set; }
/// <summary>
/// //FTP服务器的用户名
/// </summary>
private string FTPUSERNAME { get; set; }
/// <summary>
/// //FTP服务器的密码
/// </summary>
private string FTPPASSWORD { get; set; }
public FTPHelper(string ip, string username, string password, string port = "21")
{
FTPCONSTR = string.Format("{0}://{1}:{2}/", "ftp", ip, port);
FTPUSERNAME = username;
FTPPASSWORD = password;
}
#region 本地文件上传到FTP服务器
/// <summary>
/// 上传文件到远程ftp
/// </summary>
/// <param name="path">本地的文件目录</param>
/// <param name="name">文件名称</param>
/// <returns></returns>
public bool UploadFile(string path, string name)
{
FileInfo f = new FileInfo(path);
path = FTPCONSTR + name;//这个路径是我要传到ftp目录下的这个目录下
FtpWebRequest reqFtp = (FtpWebRequest)FtpWebRequest.Create(new Uri(path));
reqFtp.Method = WebRequestMethods.Ftp.UploadFile;
reqFtp.UsePassive = false;//只需要添加这一句话
reqFtp.UseBinary = true;
reqFtp.Credentials = new NetworkCredential(FTPUSERNAME, FTPPASSWORD);
reqFtp.KeepAlive = false;
//reqFtp.Method = WebRequestMethods.Ftp.UploadFile;
reqFtp.ContentLength = f.Length;
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
FileStream fs = f.OpenRead();
try
{
Stream strm = reqFtp.GetRequestStream();
contentLen = fs.Read(buff, 0, buffLength);
while (contentLen != 0)
{
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
strm.Close();
fs.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
#endregion
}
}
下面是对您提供的FTPHelper
类的详细解释。这个类封装了与FTP服务器交互的基本功能,特别是将本地文件上传到FTP服务器。
命名空间与引用
using
语句引入了必要的命名空间,以便能够使用网络操作、文件I/O、异常处理等功能。namespace TemplateProject.ClassFiles
定义了该类所属的命名空间。
FTPHelper 类
FTPHelper
类是一个帮助类,用于处理与FTP服务器的交互。
私有属性
FTPCONSTR
:存储FTP服务器的连接字符串,包括协议(ftp)、IP地址、端口号和根目录。FTPUSERNAME
:存储用于连接FTP服务器的用户名。FTPPASSWORD
:存储用于连接FTP服务器的密码。
构造函数
public FTPHelper(string ip, string username, string password, string port = "21")
:类的构造函数,接收FTP服务器的IP地址、用户名、密码和端口号(默认为21)。构造函数初始化私有属性。
方法
-
#region 本地文件上传到FTP服务器
:这是一个区域标签,用于在代码编辑器中组织相关代码。它并不影响代码的逻辑。 -
public bool UploadFile(string path, string name)
:这是一个公共方法,用于将本地文件上传到FTP服务器。它接收两个参数:本地文件的路径(path
)和要上传到FTP服务器的文件名(name
)。方法返回一个布尔值,指示上传是否成功。- 在方法内部,首先使用
FileInfo
类获取本地文件的信息。 - 然后,构造FTP服务器的完整路径,包括连接字符串和文件名。
- 使用
FtpWebRequest
类创建一个FTP请求,并设置其属性,如方法(上传文件)、是否使用被动模式、是否使用二进制模式、凭据(用户名和密码)等。 - 打开本地文件进行读取,并获取FTP请求的请求流。
- 通过循环读取本地文件的内容,并将其写入到FTP请求的流中,直到文件全部上传完毕。
- 如果上传成功,方法返回
true
;如果发生异常,捕获异常并返回false
。
- 在方法内部,首先使用
注意事项
- 在使用FTP时,需要注意FTP服务器的配置,特别是被动模式(
UsePassive
)的设置。在某些网络配置中,主动模式可能会导致连接问题。 - 代码中硬编码了FTP协议("ftp")和端口号(在构造函数中有默认值"21"),这在实际应用中可能需要根据实际情况进行调整。
- 异常处理仅简单地返回
false
,这在实际应用中可能不足以提供足够的错误信息。更好的做法是将异常信息记录下来,或者通过某种方式返回给调用者。 - 代码中没有显示地关闭
FtpWebRequest
的响应流(因为在这个例子中,我们不需要读取响应),但在某些情况下,确保正确关闭所有流和资源是一个好习惯。在这个例子中,由于我们只关心写入请求流,所以不需要读取响应流。然而,在编写网络代码时,总是要注意资源的释放,以避免资源泄露。 - 代码中使用了
FileStream
和Stream
,它们实现了IDisposable
接口。在实际应用中,最好使用using
语句来确保这些资源被正确释放。在这个例子中,由于代码结构的原因,没有使用using
语句,但在实际应用中,这是一个好习惯。
52. 序列化时被忽略的属性 [Newtonsoft.Json.JsonIgnore]
53. 命令属性 public ICommand CreatProjectCommand => new Models.Core.RelayCommand(CreatProject);
这行代码是C#中的一个表达式体成员(expression-bodied member),它定义了一个名为CreatProjectCommand
的只读属性(由于使用了=>
符号,这通常意味着属性是只读的,并且其值是通过一个表达式计算得出的)。这个属性返回了一个ICommand
接口的实例。
让我们分解这行代码:
-
public ICommand CreatProjectCommand
:public
:这是一个访问修饰符,表示这个属性是公开的,可以在类的外部访问。ICommand
:这是属性的返回类型,ICommand
是一个接口,通常用于定义命令模式的实现。在WPF、Xamarin、Uno Platform等XAML-based的应用程序中,ICommand
接口广泛用于绑定用户界面的按钮或其他可交互元素到业务逻辑或数据操作。CreatProjectCommand
:这是属性的名称。
-
=>
:- 这是C# 6.0及更高版本中引入的表达式体成员语法。它允许你以更简洁的方式定义只读属性、方法或其他成员。在这个上下文中,它表示
CreatProjectCommand
属性的值是通过后面的表达式计算得出的。
- 这是C# 6.0及更高版本中引入的表达式体成员语法。它允许你以更简洁的方式定义只读属性、方法或其他成员。在这个上下文中,它表示
-
new Models.Core.RelayCommand(CreatProject);
:- 这部分是一个对象创建表达式,它创建了一个
Models.Core.RelayCommand
的实例。 Models.Core.RelayCommand
:这是RelayCommand
类的完全限定名,它实现了ICommand
接口。RelayCommand
是一个常见的命令实现,用于在MVVM(Model-View-ViewModel)架构中桥接ViewModel和View。(CreatProject)
:这是传递给RelayCommand
构造函数的参数。这里假设CreatProject
是当前类中的一个方法,它符合RelayCommand
构造函数所期望的委托签名(通常是Action
或Action<T>
)。这意味着当命令被执行时(例如,当用户点击了一个绑定到这个命令的按钮),CreatProject
方法将被调用。
- 这部分是一个对象创建表达式,它创建了一个
综上所述,这行代码定义了一个名为CreatProjectCommand
的只读属性,它返回一个新的RelayCommand
实例,该实例在执行时会调用当前类中的CreatProject
方法。这种模式在MVVM应用程序中非常常见,用于将用户交互(如按钮点击)与业务逻辑或数据操作分离。
54. ViewModel 如何响应用户交互
using HikVision.DataAccess.Core;
using VisionInspection.Models.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace VisionInspection.ViewModels
{
public class ControlAndDebugPageViewModel : ViewModelBase
{
private string childPageUri;
public string ChildPageUri
{
get { return childPageUri; }
set { childPageUri = value; RaisedPropertyChanged(); }
}
public ICommand ShowPageCommand => new RelayCommand<string>(ShowPage);
private void ShowPage(string uri)
{
ChildPageUri = uri;
}
}
}
在提供的代码中,ControlAndDebugPageViewModel
类是一个 ViewModel,它继承自 ViewModelBase
(这个基类可能提供了如 RaisedPropertyChanged
方法等用于实现 INotifyPropertyChanged 接口的功能)。ViewModel 的职责之一是作为 View(视图)和 Model(模型)之间的桥梁,同时提供用户交互的回调函数。
在这个例子中,ControlAndDebugPageViewModel
类通过以下方式向外提供用户动作的回调函数:
-
定义 ICommand 类型的属性:
public ICommand ShowPageCommand
:这是一个公开的ICommand
类型属性,名为ShowPageCommand
。它使用表达式体成员语法(=>
)来返回一个新创建的RelayCommand<string>
实例。RelayCommand<string>
是一个泛型命令类,它接受一个Action<T>
类型的委托作为参数,在这个例子中是ShowPage
方法,并且T
被指定为string
类型。
-
创建 RelayCommand 实例:
new RelayCommand<string>(ShowPage)
:这里创建了一个RelayCommand<string>
的实例,并将ShowPage
方法作为执行逻辑传递给构造函数。RelayCommand
类实现了ICommand
接口,这意味着它提供了Execute
和CanExecute
方法。在这个例子中,CanExecute
方法可能默认返回true
(除非在RelayCommand
的构造函数中提供了额外的逻辑),而Execute
方法会在命令被触发时调用ShowPage
方法。
-
定义 ShowPage 方法:
private void ShowPage(string uri)
:这是一个私有方法,它接受一个string
类型的参数uri
。- 当
ShowPageCommand
被执行时(例如,用户点击了一个绑定到这个命令的按钮),ShowPage
方法将被调用,并且传入的参数(通常是按钮的CommandParameter
或其他来源的参数)将作为uri
参数传递给该方法。 - 在
ShowPage
方法内部,它更新了ChildPageUri
属性的值。由于ChildPageUri
属性实现了 INotifyPropertyChanged 接口的通知机制(通过调用RaisedPropertyChanged
方法),因此任何绑定到ChildPageUri
属性的 UI 元素都会收到通知并更新其显示。
通过这种方式,ControlAndDebugPageViewModel
类提供了一个用户动作的回调函数(ShowPage
),该函数可以通过 ShowPageCommand
命令属性被触发。在 MVVM 架构中,这允许 View(视图)通过绑定机制与 ViewModel(视图模型)中的命令进行交互,而无需直接引用 ViewModel 中的方法或属性。这有助于保持 View 和 ViewModel 之间的松耦合,并遵循了 MVVM 架构的原则。
55. 手动释放属性被修改信息
private ObservableCollection<RecordInfo> testInfos { get; set; } = new ObservableCollection<RecordInfo>();
public ObservableCollection<RecordInfo> TestInfos
{
get => testInfos;
set
{
testInfos = value;
RaisedPropertyChanged(nameof(TestInfos));
}
}
protected void RaisedPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
这段代码是C#中用于实现INotifyPropertyChanged接口的一部分,通常用在MVVM(Model-View-ViewModel)架构的ViewModel层,以便在属性值改变时通知绑定的UI元素进行更新。下面是对代码的详细解释:
- 私有字段定义:
csharp复制代码
private ObservableCollection<RecordInfo> testInfos { get; set; } = new ObservableCollection<RecordInfo>();
private
:这是一个访问修饰符,表示testInfos
字段只能在当前类内部访问。ObservableCollection<RecordInfo>
:这是testInfos
字段的类型,它是一个集合,可以动态地添加、移除或更新元素,并且当集合发生变化时,会自动通知绑定到该集合的UI元素。RecordInfo
是集合中元素的类型。{ get; set; }
:这是自动实现的属性访问器,允许在类的内部和外部获取或设置testInfos
字段的值。但由于字段被初始化为一个新实例,并且没有在类的外部设置它的值的逻辑,所以set
访问器实际上在这个例子中没有被使用。= new ObservableCollection<RecordInfo>()
:这是字段的初始化,它创建了一个ObservableCollection<RecordInfo>
的新实例,并将其赋值给testInfos
字段。
- 公共属性定义:
csharp复制代码
public ObservableCollection<RecordInfo> TestInfos
{
get => testInfos;
set
{
testInfos = value;
RaisedPropertyChanged(nameof(TestInfos));
}
}
-
public
:这是一个访问修饰符,表示TestInfos
属性可以在类的外部访问。 -
ObservableCollection<RecordInfo>
:这是TestInfos
属性的类型,与testInfos
字段的类型相同。 -
get => testInfos;
:这是属性的get
访问器,它使用表达式体成员语法返回testInfos
字段的值。 -
set
:这是属性的set
访问器,它允许在外部设置TestInfos
属性的值。当属性值改变时,它会执行以下操作:testInfos = value;
:将传入的value
赋值给testInfos
字段。RaisedPropertyChanged(nameof(TestInfos));
:调用RaisedPropertyChanged
方法,通知绑定到TestInfos
属性的UI元素属性值已经改变。nameof(TestInfos)
是一个编译器功能,它返回属性名的字符串表示。
- PropertyChanged事件触发方法:
csharp复制代码
protected void RaisedPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
protected
:这是一个访问修饰符,表示RaisedPropertyChanged
方法可以在当前类和派生类中访问。[CallerMemberName] string name = null
:这是一个可选参数,带有CallerMemberName
特性。当方法被调用时,如果name
参数没有被显式提供值,编译器会自动将调用该方法的成员(如属性名)作为参数值。这避免了在调用方法时手动传递属性名的需要。PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
:这是事件触发的代码。PropertyChanged
是一个事件,它遵循.NET的事件模式。?.
是C#的空条件运算符,它允许在尝试调用事件处理程序之前检查事件是否为null
(即没有事件处理程序被附加)。如果PropertyChanged
不为null
,则调用其Invoke
方法,并传递当前对象(this
)和一个包含属性名的新PropertyChangedEventArgs
实例作为参数。
注意:在提供的代码中,TestInfos
属性的set
访问器实际上可能不会被使用,因为testInfos
字段在初始化后没有在类的外部被重新赋值的逻辑。通常,在MVVM中,你会直接操作ObservableCollection<T>
实例(如添加、移除或更新元素),而不是替换整个集合实例。替换整个集合实例可能会导致绑定到该集合的UI元素失去与集合的同步。如果你需要通知UI元素集合中的元素已经改变,你应该使用ObservableCollection<T>
提供的内置机制(如添加或移除元素时自动通知)。
这三行代码实际上是在属性值变化的时候通知绑定的 UI 元素。